Skip to content

Commit 63130d4

Browse files
committedApr 5, 2018
Another piece of random useless code in QGIS. Happy April 1st!
Type "bored" into the coordinates box (best results with a map loaded in canvas) Click on tiles to move them to the empty space. Click on the empty tile to toggle tile numbers.
1 parent 7d65ee9 commit 63130d4

File tree

6 files changed

+259
-0
lines changed

6 files changed

+259
-0
lines changed
 

‎src/app/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ SET(QGIS_APP_SRCS
5757
qgsmapcanvasdockwidget.cpp
5858
qgsmaplayerstyleguiutils.cpp
5959
qgsmapsavedialog.cpp
60+
qgspuzzlewidget.cpp
6061
qgsversionmigration.cpp
6162
qgsrulebasedlabelingwidget.cpp
6263
qgssavestyletodbdialog.cpp
@@ -269,6 +270,7 @@ SET (QGIS_APP_MOC_HDRS
269270
qgsmapcanvasdockwidget.h
270271
qgsmaplayerstyleguiutils.h
271272
qgsmapsavedialog.h
273+
qgspuzzlewidget.h
272274
qgsrulebasedlabelingwidget.h
273275
qgssavestyletodbdialog.h
274276
qgssnappinglayertreemodel.h

‎src/app/qgisapp.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
242242
#include "qgspluginmanager.h"
243243
#include "qgspluginregistry.h"
244244
#include "qgspointxy.h"
245+
#include "qgspuzzlewidget.h"
245246
#include "qgsruntimeprofiler.h"
246247
#include "qgshandlebadlayers.h"
247248
#include "qgsprintlayout.h"
@@ -1335,6 +1336,19 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
13351336
connect( QgsProject::instance(), &QgsProject::isDirtyChanged, mActionRevertProject, toggleRevert );
13361337
connect( QgsProject::instance(), &QgsProject::fileNameChanged, mActionRevertProject, toggleRevert );
13371338

1339+
// the most important part of the initialization: make sure that people can play puzzle if they need
1340+
QgsPuzzleWidget *puzzleWidget = new QgsPuzzleWidget( mMapCanvas );
1341+
mCentralContainer->insertWidget( 2, puzzleWidget );
1342+
connect( mCoordsEdit, &QgsStatusBarCoordinatesWidget::weAreBored, this, [ this, puzzleWidget ]
1343+
{
1344+
puzzleWidget->letsGetThePartyStarted();
1345+
mCentralContainer->setCurrentIndex( 2 );
1346+
} );
1347+
connect( puzzleWidget, &QgsPuzzleWidget::done, this, [ this ]
1348+
{
1349+
mCentralContainer->setCurrentIndex( 0 );
1350+
} );
1351+
13381352
} // QgisApp ctor
13391353

13401354
QgisApp::QgisApp()

‎src/app/qgspuzzlewidget.cpp

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/***************************************************************************
2+
qgspuzzlewidget.cpp
3+
--------------------------------------
4+
Date : 1st of April 2018
5+
Copyright : (C) 2018 by Martin Dobias
6+
Email : wonder dot sk at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgspuzzlewidget.h"
17+
18+
#include "qgsmapcanvas.h"
19+
20+
#include <QGraphicsPixmapItem>
21+
#include <QMessageBox>
22+
23+
24+
static bool testSolved( QVector<int> &positions )
25+
{
26+
for ( int i = 0; i < positions.count() - 1; ++i )
27+
{
28+
if ( positions[i] != i )
29+
return false;
30+
}
31+
return true;
32+
}
33+
34+
static int findEmpty( QVector<int> &positions )
35+
{
36+
for ( int i = 0; i < positions.count(); ++i )
37+
{
38+
if ( positions[i] == -1 )
39+
return i;
40+
}
41+
Q_ASSERT( false );
42+
return -1;
43+
}
44+
45+
// half of the possible permutations lead to an unsolvable puzzle, so we initialize the puzzle by
46+
// doing valid moves
47+
// https://en.wikipedia.org/wiki/15_puzzle
48+
static void shuffle( QVector<int> &positions, int count )
49+
{
50+
int size = sqrt( positions.count() );
51+
int idxEmpty = findEmpty( positions );
52+
int cEmpty = idxEmpty % size, rEmpty = idxEmpty / size;
53+
int moveX[] = { 0, 0, 1, -1 };
54+
int moveY[] = { 1, -1, 0, 0 };
55+
int cOther, rOther;
56+
for ( int i = 0; i < count; ++i )
57+
{
58+
do
59+
{
60+
int move = qrand() % 4;
61+
cOther = cEmpty + moveX[move];
62+
rOther = rEmpty + moveY[move];
63+
}
64+
while ( cOther < 0 || cOther >= size || rOther < 0 || rOther >= size );
65+
66+
int idxOther = rOther * size + cOther;
67+
std::swap( positions[idxEmpty], positions[idxOther] );
68+
idxEmpty = idxOther;
69+
cEmpty = idxEmpty % size;
70+
rEmpty = idxEmpty / size;
71+
}
72+
}
73+
74+
QgsPuzzleWidget::QgsPuzzleWidget( QgsMapCanvas *canvas )
75+
: mCanvas( canvas )
76+
{
77+
setInteractive( false );
78+
setBackgroundBrush( QBrush( Qt::lightGray ) );
79+
}
80+
81+
void QgsPuzzleWidget::mousePressEvent( QMouseEvent *event )
82+
{
83+
int idxEmpty = findEmpty( mPositions );
84+
int rEmpty = idxEmpty / mSize;
85+
int cEmpty = idxEmpty % mSize;
86+
int cMouse = event->pos().x() / mTileWidth;
87+
int rMouse = event->pos().y() / mTileHeight;
88+
int idxMouse = rMouse * mSize + cMouse;
89+
int dx = cMouse - cEmpty;
90+
int dy = rMouse - rEmpty;
91+
92+
if ( ( dx == 0 && qAbs( dy ) == 1 ) || ( dy == 0 && qAbs( dx ) == 1 ) )
93+
{
94+
std::swap( mPositions[idxEmpty], mPositions[idxMouse] );
95+
updateTilePositions();
96+
if ( testSolved( mPositions ) )
97+
{
98+
QMessageBox::information( nullptr, tr( "QGIS" ), tr( "Well done!\n\nNow let's get back to work, shall we?" ) );
99+
emit done();
100+
}
101+
}
102+
else if ( dx == 0 && dy == 0 )
103+
{
104+
// toggle text help when clicked on empty tile
105+
for ( int i = 0; i < mTextItems.count(); ++i )
106+
mTextItems[i]->setVisible( !mTextItems[i]->isVisible() );
107+
}
108+
}
109+
110+
void QgsPuzzleWidget::letsGetThePartyStarted()
111+
{
112+
mPositions.clear();
113+
delete mScene;
114+
mItems.clear();
115+
mTextItems.clear();
116+
117+
mScene = new QGraphicsScene;
118+
119+
QTemporaryFile f;
120+
f.open();
121+
f.close();
122+
123+
QString filename( f.fileName() );
124+
mCanvas->saveAsImage( filename );
125+
126+
QPixmap pixmap;
127+
pixmap.load( filename );
128+
129+
int tileWidth = pixmap.width() / mSize;
130+
int tileHeight = pixmap.height() / mSize;
131+
mTileWidth = tileWidth;
132+
mTileHeight = tileHeight;
133+
134+
for ( int row = 0; row < mSize; ++row )
135+
for ( int col = 0; col < mSize; ++col )
136+
{
137+
QGraphicsPixmapItem *item = new QGraphicsPixmapItem;
138+
item->setPixmap( pixmap.copy( col * tileWidth, row * tileHeight, tileWidth, tileHeight ) );
139+
mScene->addItem( item ); // takes ownership
140+
mItems.append( item );
141+
142+
QGraphicsSimpleTextItem *textItem = new QGraphicsSimpleTextItem( QString::number( row * mSize + col + 1 ), item );
143+
QRectF textRect = textItem->boundingRect();
144+
textItem->setPos( ( tileWidth - textRect.width() ) / 2, ( tileHeight - textRect.height() ) / 2 );
145+
textItem->setVisible( false );
146+
mTextItems.append( textItem );
147+
}
148+
149+
// extra lines to show tile split points
150+
for ( int i = 1; i < mSize; ++i )
151+
{
152+
QGraphicsLineItem *lineHorz = new QGraphicsLineItem( 0, i * tileHeight, tileWidth * mSize, i * tileHeight );
153+
QGraphicsLineItem *lineVert = new QGraphicsLineItem( i * tileWidth, 0, i * tileWidth, tileHeight * mSize );
154+
lineHorz->setZValue( 10 );
155+
lineVert->setZValue( 10 );
156+
mScene->addItem( lineHorz );
157+
mScene->addItem( lineVert );
158+
}
159+
160+
// initialize positions
161+
for ( int i = 0; i < mSize * mSize; ++i )
162+
mPositions << i;
163+
mPositions[mSize * mSize - 1] = -1;
164+
shuffle( mPositions, 1000 );
165+
166+
mItems[mSize * mSize - 1]->setVisible( false ); // hide item for the missing piece
167+
168+
updateTilePositions();
169+
170+
setScene( mScene );
171+
}
172+
173+
void QgsPuzzleWidget::updateTilePositions()
174+
{
175+
for ( int i = 0; i < mSize * mSize; ++i )
176+
{
177+
int itemIndex = mPositions[i];
178+
if ( itemIndex == -1 )
179+
continue; // empty tile
180+
181+
int r = i / mSize, c = i % mSize;
182+
int x = c * mTileWidth, y = r * mTileHeight;
183+
mItems[itemIndex]->setPos( x, y );
184+
}
185+
}

‎src/app/qgspuzzlewidget.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/***************************************************************************
2+
qgspuzzlewidget.h
3+
--------------------------------------
4+
Date : 1st of April 2018
5+
Copyright : (C) 2018 by Martin Dobias
6+
Email : wonder dot sk at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSPUZZLEWIDGET_H
17+
#define QGSPUZZLEWIDGET_H
18+
19+
#include <QGraphicsView>
20+
21+
class QgsMapCanvas;
22+
23+
24+
class QgsPuzzleWidget : public QGraphicsView
25+
{
26+
Q_OBJECT
27+
public:
28+
explicit QgsPuzzleWidget( QgsMapCanvas *canvas = nullptr );
29+
30+
protected:
31+
void mousePressEvent( QMouseEvent *event ) override;
32+
33+
signals:
34+
void done();
35+
36+
public slots:
37+
void letsGetThePartyStarted();
38+
39+
private:
40+
void updateTilePositions();
41+
42+
private:
43+
QgsMapCanvas *mCanvas = nullptr;
44+
QGraphicsScene *mScene = nullptr;
45+
int mSize = 4; //!< Number of items in one row/column
46+
int mTileWidth = 0, mTileHeight = 0;
47+
QVector<QGraphicsItem *> mItems;
48+
QVector<QGraphicsItem *> mTextItems;
49+
QVector<int> mPositions; //!< Indices of items (-1 where the piece is missing)
50+
};
51+
52+
#endif // QGSPUZZLEWIDGET_H

‎src/app/qgsstatusbarcoordinateswidget.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ void QgsStatusBarCoordinatesWidget::validateCoordinates()
138138
refreshMapCanvas();
139139
return;
140140
}
141+
else if ( mLineEdit->text() == QStringLiteral( "bored" ) )
142+
{
143+
// it's friday afternoon and too late to start another piece of work...
144+
emit weAreBored();
145+
}
141146

142147
bool xOk = false;
143148
bool yOk = false;

‎src/app/qgsstatusbarcoordinateswidget.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class APP_EXPORT QgsStatusBarCoordinatesWidget : public QWidget
5252

5353
signals:
5454
void coordinatesChanged();
55+
void weAreBored();
5556

5657
private slots:
5758
void showMouseCoordinates( const QgsPointXY &p );

0 commit comments

Comments
 (0)
Please sign in to comment.