Skip to content

Commit f699b69

Browse files
committed
feat: add /dizz and /flick slash commands
1 parent b578601 commit f699b69

File tree

6 files changed

+166
-54
lines changed

6 files changed

+166
-54
lines changed

qchat/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
# QChat cheatcodes
3030
CHEATCODE_DIZZY: str = "givemesomecheese"
31+
CHEATCODE_FLICK: str = "lookattheflickofqgis"
3132
CHEATCODE_IAMAROBOT: str = "iamarobot"
3233
CHEATCODE_10OCLOCK: str = "its10oclock"
3334
CHEATCODE_QGIS_PRO_LICENSE: str = "qgisprolicense"

qchat/gui/dck_qchat.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
ADMIN_MESSAGES_NICKNAME,
3232
CHEATCODE_10OCLOCK,
3333
CHEATCODE_DIZZY,
34+
CHEATCODE_FLICK,
3435
CHEATCODE_IAMAROBOT,
3536
CHEATCODE_QGIS_PRO_LICENSE,
3637
CHEATCODES,
@@ -45,6 +46,7 @@
4546
QCHAT_NICKNAME_MAXLENGTH_DEFAULT,
4647
QCHAT_NICKNAME_MINLENGTH,
4748
)
49+
from qchat.gui.effects import dizzy, flick_of_the_wrist
4850
from qchat.gui.qchat_tree_widget_items import (
4951
MESSAGE_COLUMN,
5052
QChatAdminTreeWidgetItem,
@@ -71,7 +73,6 @@
7173
)
7274
from qchat.logic.qchat_websocket import QChatWebsocket
7375
from qchat.logic.slash_commands import SlashCommandHandler
74-
from qchat.tasks.dizzy import DizzyTask
7576
from qchat.toolbelt import PlgLogger, PlgOptionsManager
7677
from qchat.toolbelt.commons import open_url_in_browser, play_resource_sound
7778
from qchat.toolbelt.preferences import PlgSettingsStructure
@@ -910,6 +911,9 @@ def on_send_button_clicked(self) -> None:
910911
if action_result and action_result[0] == "show_message":
911912
# Show message locally via QMessageBox
912913
QMessageBox.information(self, self.tr("QChat"), action_result[1])
914+
if action_result and action_result[0] == "show_message_bar":
915+
# Show message locally in QGIS message bar
916+
self.iface.messageBar().pushInfo(self.tr("QChat"), action_result[1])
913917
return
914918

915919
# If there's a message text, send it to chat
@@ -1054,8 +1058,12 @@ def check_cheatcode(self, text: str) -> bool:
10541058
"""
10551059
# make QGIS shuffle for a few seconds
10561060
if text == CHEATCODE_DIZZY:
1057-
task = DizzyTask(f"Cheatcode activation: {CHEATCODE_DIZZY}", self.iface)
1058-
self.task_manager.addTask(task)
1061+
dizzy()
1062+
return True
1063+
1064+
# make QGIS flick the wrist for a few seconds
1065+
if text == CHEATCODE_FLICK:
1066+
flick_of_the_wrist()
10591067
return True
10601068

10611069
# QGIS pro license expiration message

qchat/gui/effects.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import random
2+
from datetime import datetime, timedelta
3+
from typing import Tuple
4+
5+
import numpy as np
6+
from qgis.PyQt.QtCore import QTimer
7+
from qgis.PyQt.QtGui import QTransform
8+
from qgis.utils import iface
9+
10+
11+
def dizzy(
12+
duration: int = 3,
13+
d: int = 12,
14+
r: int = 6,
15+
interval: int = 50,
16+
should_start: bool = True,
17+
) -> QTimer:
18+
"""Dizz command, make QGIS shake.
19+
20+
:param duration: duration in seconds
21+
:param d: max offset in pixels
22+
:param r: max rotation in degrees
23+
:param interval: refresh interval in milliseconds
24+
:param should_start: whether to start the timer immediately
25+
:return: QTimer instance
26+
"""
27+
28+
canvas = iface.mapCanvas()
29+
stop_time = datetime.now() + timedelta(seconds=duration)
30+
31+
initial_scene_rect = canvas.sceneRect()
32+
initial_transform = canvas.transform()
33+
34+
timer = QTimer()
35+
timer.setInterval(interval)
36+
37+
def dizz():
38+
if datetime.now() >= stop_time:
39+
timer.stop()
40+
canvas.setSceneRect(initial_scene_rect)
41+
canvas.setTransform(initial_transform)
42+
canvas.refresh()
43+
return
44+
45+
rect = initial_scene_rect.translated(
46+
random.randint(-d, d), random.randint(-d, d)
47+
)
48+
canvas.setSceneRect(rect)
49+
50+
matrix = QTransform()
51+
matrix.rotate(random.randint(-r, r))
52+
canvas.setTransform(matrix)
53+
54+
canvas.refresh()
55+
56+
timer.timeout.connect(dizz)
57+
58+
if should_start:
59+
timer.start()
60+
61+
return timer
62+
63+
64+
def coords8(size: int, nb_points: int = 100) -> Tuple[int, int]:
65+
t = np.linspace(0, 2 * np.pi, nb_points)
66+
x = size * np.sin(t)
67+
y = size * np.cos(t) / 2
68+
return (x, y)
69+
70+
71+
def flick_of_the_wrist(
72+
duration: int = 3,
73+
size: int = 500,
74+
nb_points: int = 20,
75+
interval: int = 50,
76+
should_start: bool = True,
77+
) -> QTimer:
78+
canvas = iface.mapCanvas()
79+
stop_time = datetime.now() + timedelta(seconds=duration)
80+
81+
center = canvas.center()
82+
cx, cy = center.x(), center.y()
83+
coords = coords8(size, nb_points)
84+
85+
i = 0
86+
up = True
87+
88+
timer = QTimer()
89+
timer.setInterval(interval)
90+
91+
def flick():
92+
nonlocal cx, cy, i, up
93+
94+
if datetime.now() >= stop_time:
95+
timer.stop()
96+
return
97+
98+
nx, ny = coords[0][i], coords[1][i]
99+
dx, dy = nx - cx, ny - cy
100+
101+
rect = canvas.sceneRect()
102+
rect.moveTo(dx, dy)
103+
canvas.setSceneRect(rect)
104+
105+
cx = nx
106+
cy = ny
107+
108+
if up:
109+
i += 1
110+
if i >= nb_points - 1:
111+
up = False
112+
else:
113+
i -= 1
114+
if i <= 0:
115+
up = True
116+
117+
timer.timeout.connect(flick)
118+
119+
if should_start:
120+
timer.start()
121+
122+
return timer

qchat/logic/slash_commands.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
from dataclasses import dataclass
88
from typing import Callable, Optional
99

10+
from qchat.gui.effects import dizzy, flick_of_the_wrist
11+
1012

1113
@dataclass
1214
class SlashCommandResult:
@@ -55,6 +57,8 @@ class SlashCommandHandler:
5557
"flip": "Flip a coin (heads or tails)",
5658
"roll": "Roll dice (e.g. /roll 2d20)",
5759
"8ball": "Ask the magic 8-ball",
60+
"dizz": "Let QGIS shake",
61+
"flick": "Look at QGIS flicking the wrist",
5862
}
5963

6064
def __init__(self):
@@ -68,6 +72,8 @@ def __init__(self):
6872
"flip": self.cmd_flip,
6973
"roll": self.cmd_roll,
7074
"8ball": self.cmd_8ball,
75+
"dizz": self.cmd_dizz,
76+
"flick": self.cmd_flick,
7177
}
7278

7379
def get_command_list(self) -> list[str]:
@@ -222,6 +228,32 @@ def cmd_8ball(self, args: str) -> SlashCommandResult:
222228
message = f"{emoji} {answer}"
223229
return SlashCommandResult(success=True, message_text=message)
224230

231+
def cmd_dizz(self, args: str) -> SlashCommandResult:
232+
"""Dizz command, makes QGIS shake.
233+
234+
:param args: optional message displayed in message bar
235+
:return: SlashCommandResult
236+
"""
237+
238+
dizzy()
239+
240+
return SlashCommandResult(
241+
success=True, local_action=lambda: ("show_message_bar", args)
242+
)
243+
244+
def cmd_flick(self, args: str) -> SlashCommandResult:
245+
"""Flick command, QGIS is flicking the wrist.
246+
247+
:param args: optional message displayed in message bar
248+
:return: SlashCommandResult
249+
"""
250+
251+
flick_of_the_wrist()
252+
253+
return SlashCommandResult(
254+
success=True, local_action=lambda: ("show_message_bar", args)
255+
)
256+
225257
def cmd_list(self, args: str) -> SlashCommandResult:
226258
"""List all available commands.
227259

qchat/tasks/__init__.py

Whitespace-only changes.

qchat/tasks/dizzy.py

Lines changed: 0 additions & 51 deletions
This file was deleted.

0 commit comments

Comments
 (0)