sqlshell 0.4.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. sqlshell/__init__.py +84 -0
  2. sqlshell/__main__.py +4926 -0
  3. sqlshell/ai_autocomplete.py +392 -0
  4. sqlshell/ai_settings_dialog.py +337 -0
  5. sqlshell/context_suggester.py +768 -0
  6. sqlshell/create_test_data.py +152 -0
  7. sqlshell/data/create_test_data.py +137 -0
  8. sqlshell/db/__init__.py +6 -0
  9. sqlshell/db/database_manager.py +1318 -0
  10. sqlshell/db/export_manager.py +188 -0
  11. sqlshell/editor.py +1166 -0
  12. sqlshell/editor_integration.py +127 -0
  13. sqlshell/execution_handler.py +421 -0
  14. sqlshell/menus.py +262 -0
  15. sqlshell/notification_manager.py +370 -0
  16. sqlshell/query_tab.py +904 -0
  17. sqlshell/resources/__init__.py +1 -0
  18. sqlshell/resources/icon.png +0 -0
  19. sqlshell/resources/logo_large.png +0 -0
  20. sqlshell/resources/logo_medium.png +0 -0
  21. sqlshell/resources/logo_small.png +0 -0
  22. sqlshell/resources/splash_screen.gif +0 -0
  23. sqlshell/space_invaders.py +501 -0
  24. sqlshell/splash_screen.py +405 -0
  25. sqlshell/sqlshell/__init__.py +5 -0
  26. sqlshell/sqlshell/create_test_data.py +118 -0
  27. sqlshell/sqlshell/create_test_databases.py +96 -0
  28. sqlshell/sqlshell_demo.png +0 -0
  29. sqlshell/styles.py +257 -0
  30. sqlshell/suggester_integration.py +330 -0
  31. sqlshell/syntax_highlighter.py +124 -0
  32. sqlshell/table_list.py +996 -0
  33. sqlshell/ui/__init__.py +6 -0
  34. sqlshell/ui/bar_chart_delegate.py +49 -0
  35. sqlshell/ui/filter_header.py +469 -0
  36. sqlshell/utils/__init__.py +16 -0
  37. sqlshell/utils/profile_cn2.py +1661 -0
  38. sqlshell/utils/profile_column.py +2635 -0
  39. sqlshell/utils/profile_distributions.py +616 -0
  40. sqlshell/utils/profile_entropy.py +347 -0
  41. sqlshell/utils/profile_foreign_keys.py +779 -0
  42. sqlshell/utils/profile_keys.py +2834 -0
  43. sqlshell/utils/profile_ohe.py +934 -0
  44. sqlshell/utils/profile_ohe_advanced.py +754 -0
  45. sqlshell/utils/profile_ohe_comparison.py +237 -0
  46. sqlshell/utils/profile_prediction.py +926 -0
  47. sqlshell/utils/profile_similarity.py +876 -0
  48. sqlshell/utils/search_in_df.py +90 -0
  49. sqlshell/widgets.py +400 -0
  50. sqlshell-0.4.4.dist-info/METADATA +441 -0
  51. sqlshell-0.4.4.dist-info/RECORD +54 -0
  52. sqlshell-0.4.4.dist-info/WHEEL +5 -0
  53. sqlshell-0.4.4.dist-info/entry_points.txt +2 -0
  54. sqlshell-0.4.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1 @@
1
+ # SQLShell resources package initialization
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,501 @@
1
+ """
2
+ Space Invaders mini-game for SQLShell About menu.
3
+ A classic arcade game implemented in PyQt6.
4
+ """
5
+
6
+ import random
7
+ from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QDialog
8
+ from PyQt6.QtCore import Qt, QTimer, QRectF, pyqtSignal
9
+ from PyQt6.QtGui import QPainter, QColor, QFont, QBrush, QPen, QKeyEvent
10
+
11
+
12
+ class SpaceInvadersGame(QWidget):
13
+ """The main Space Invaders game widget."""
14
+
15
+ game_over_signal = pyqtSignal(int) # Signal emitted with final score
16
+
17
+ def __init__(self, parent=None):
18
+ super().__init__(parent)
19
+ self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
20
+ self.setMinimumSize(500, 600)
21
+
22
+ # Game state
23
+ self.game_running = False
24
+ self.game_over = False
25
+ self.score = 0
26
+ self.lives = 3
27
+ self.level = 1
28
+
29
+ # Player
30
+ self.player_x = 250
31
+ self.player_width = 50
32
+ self.player_height = 20
33
+ self.player_speed = 8
34
+
35
+ # Bullets
36
+ self.bullets = []
37
+ self.bullet_speed = 10
38
+ self.can_shoot = True
39
+ self.shoot_cooldown = 250 # ms
40
+
41
+ # Invaders
42
+ self.invaders = []
43
+ self.invader_direction = 1
44
+ self.invader_speed = 2
45
+ self.invader_drop = 20
46
+ self.invader_width = 35
47
+ self.invader_height = 25
48
+
49
+ # Invader bullets
50
+ self.invader_bullets = []
51
+ self.invader_bullet_speed = 5
52
+
53
+ # Stars background
54
+ self.stars = [(random.randint(0, 500), random.randint(0, 600)) for _ in range(100)]
55
+
56
+ # Colors - retro arcade style
57
+ self.bg_color = QColor(5, 5, 20)
58
+ self.player_color = QColor(0, 255, 100)
59
+ self.bullet_color = QColor(255, 255, 0)
60
+ self.invader_colors = [
61
+ QColor(255, 50, 50), # Red
62
+ QColor(255, 150, 50), # Orange
63
+ QColor(255, 255, 50), # Yellow
64
+ QColor(50, 255, 255), # Cyan
65
+ ]
66
+ self.star_color = QColor(255, 255, 255, 100)
67
+
68
+ # Timers
69
+ self.game_timer = QTimer(self)
70
+ self.game_timer.timeout.connect(self.game_loop)
71
+
72
+ self.shoot_timer = QTimer(self)
73
+ self.shoot_timer.timeout.connect(self.enable_shooting)
74
+ self.shoot_timer.setSingleShot(True)
75
+
76
+ self.invader_shoot_timer = QTimer(self)
77
+ self.invader_shoot_timer.timeout.connect(self.invader_shoot)
78
+
79
+ # Key states
80
+ self.keys_pressed = set()
81
+
82
+ # Initialize game
83
+ self.init_invaders()
84
+
85
+ def init_invaders(self):
86
+ """Initialize the invader grid."""
87
+ self.invaders = []
88
+ rows = min(4, 2 + self.level // 2)
89
+ cols = min(10, 6 + self.level // 2)
90
+
91
+ start_x = (500 - cols * 45) // 2
92
+ start_y = 60
93
+
94
+ for row in range(rows):
95
+ for col in range(cols):
96
+ x = start_x + col * 45
97
+ y = start_y + row * 35
98
+ color_idx = row % len(self.invader_colors)
99
+ self.invaders.append({
100
+ 'x': x,
101
+ 'y': y,
102
+ 'color': self.invader_colors[color_idx],
103
+ 'alive': True
104
+ })
105
+
106
+ def start_game(self):
107
+ """Start the game."""
108
+ self.game_running = True
109
+ self.game_over = False
110
+ self.score = 0
111
+ self.lives = 3
112
+ self.level = 1
113
+ self.player_x = 225
114
+ self.bullets = []
115
+ self.invader_bullets = []
116
+ self.invader_direction = 1
117
+ self.invader_speed = 2
118
+ self.init_invaders()
119
+
120
+ self.game_timer.start(16) # ~60 FPS
121
+ self.invader_shoot_timer.start(1500)
122
+ self.setFocus()
123
+
124
+ def stop_game(self):
125
+ """Stop the game."""
126
+ self.game_running = False
127
+ self.game_timer.stop()
128
+ self.invader_shoot_timer.stop()
129
+
130
+ def game_loop(self):
131
+ """Main game loop."""
132
+ if not self.game_running:
133
+ return
134
+
135
+ self.handle_input()
136
+ self.update_bullets()
137
+ self.update_invaders()
138
+ self.update_invader_bullets()
139
+ self.check_collisions()
140
+ self.check_level_complete()
141
+
142
+ self.update()
143
+
144
+ def handle_input(self):
145
+ """Handle player input."""
146
+ if Qt.Key.Key_Left in self.keys_pressed or Qt.Key.Key_A in self.keys_pressed:
147
+ self.player_x = max(0, self.player_x - self.player_speed)
148
+ if Qt.Key.Key_Right in self.keys_pressed or Qt.Key.Key_D in self.keys_pressed:
149
+ self.player_x = min(500 - self.player_width, self.player_x + self.player_speed)
150
+ if Qt.Key.Key_Space in self.keys_pressed and self.can_shoot:
151
+ self.shoot()
152
+
153
+ def shoot(self):
154
+ """Fire a bullet."""
155
+ if not self.can_shoot:
156
+ return
157
+
158
+ bullet_x = self.player_x + self.player_width // 2 - 2
159
+ bullet_y = 560
160
+ self.bullets.append({'x': bullet_x, 'y': bullet_y})
161
+
162
+ self.can_shoot = False
163
+ self.shoot_timer.start(self.shoot_cooldown)
164
+
165
+ def enable_shooting(self):
166
+ """Re-enable shooting after cooldown."""
167
+ self.can_shoot = True
168
+
169
+ def invader_shoot(self):
170
+ """Random invader shoots."""
171
+ if not self.game_running:
172
+ return
173
+
174
+ alive_invaders = [inv for inv in self.invaders if inv['alive']]
175
+ if alive_invaders:
176
+ shooter = random.choice(alive_invaders)
177
+ self.invader_bullets.append({
178
+ 'x': shooter['x'] + self.invader_width // 2,
179
+ 'y': shooter['y'] + self.invader_height
180
+ })
181
+
182
+ def update_bullets(self):
183
+ """Update player bullet positions."""
184
+ for bullet in self.bullets[:]:
185
+ bullet['y'] -= self.bullet_speed
186
+ if bullet['y'] < 0:
187
+ self.bullets.remove(bullet)
188
+
189
+ def update_invader_bullets(self):
190
+ """Update invader bullet positions."""
191
+ for bullet in self.invader_bullets[:]:
192
+ bullet['y'] += self.invader_bullet_speed
193
+ if bullet['y'] > 600:
194
+ self.invader_bullets.remove(bullet)
195
+
196
+ def update_invaders(self):
197
+ """Update invader positions."""
198
+ alive_invaders = [inv for inv in self.invaders if inv['alive']]
199
+ if not alive_invaders:
200
+ return
201
+
202
+ # Check if any invader hits the edge
203
+ hit_edge = False
204
+ for inv in alive_invaders:
205
+ if self.invader_direction > 0 and inv['x'] + self.invader_width >= 490:
206
+ hit_edge = True
207
+ break
208
+ elif self.invader_direction < 0 and inv['x'] <= 10:
209
+ hit_edge = True
210
+ break
211
+
212
+ if hit_edge:
213
+ self.invader_direction *= -1
214
+ for inv in alive_invaders:
215
+ inv['y'] += self.invader_drop
216
+ else:
217
+ for inv in alive_invaders:
218
+ inv['x'] += self.invader_speed * self.invader_direction
219
+
220
+ def check_collisions(self):
221
+ """Check for collisions."""
222
+ # Player bullets vs invaders
223
+ for bullet in self.bullets[:]:
224
+ bullet_rect = QRectF(bullet['x'], bullet['y'], 4, 10)
225
+ for inv in self.invaders:
226
+ if not inv['alive']:
227
+ continue
228
+ inv_rect = QRectF(inv['x'], inv['y'], self.invader_width, self.invader_height)
229
+ if bullet_rect.intersects(inv_rect):
230
+ inv['alive'] = False
231
+ if bullet in self.bullets:
232
+ self.bullets.remove(bullet)
233
+ self.score += 10 * self.level
234
+ break
235
+
236
+ # Invader bullets vs player
237
+ player_rect = QRectF(self.player_x, 560, self.player_width, self.player_height)
238
+ for bullet in self.invader_bullets[:]:
239
+ bullet_rect = QRectF(bullet['x'], bullet['y'], 4, 10)
240
+ if bullet_rect.intersects(player_rect):
241
+ self.invader_bullets.remove(bullet)
242
+ self.lives -= 1
243
+ if self.lives <= 0:
244
+ self.end_game()
245
+
246
+ # Check if invaders reached the player
247
+ for inv in self.invaders:
248
+ if inv['alive'] and inv['y'] + self.invader_height >= 540:
249
+ self.end_game()
250
+ break
251
+
252
+ def check_level_complete(self):
253
+ """Check if all invaders are destroyed."""
254
+ alive_invaders = [inv for inv in self.invaders if inv['alive']]
255
+ if not alive_invaders:
256
+ self.level += 1
257
+ self.invader_speed = min(6, 2 + self.level * 0.5)
258
+ self.init_invaders()
259
+ self.invader_bullets = []
260
+ # Bonus points for completing level
261
+ self.score += 100 * self.level
262
+
263
+ def end_game(self):
264
+ """End the game."""
265
+ self.game_over = True
266
+ self.stop_game()
267
+ self.game_over_signal.emit(self.score)
268
+ self.update()
269
+
270
+ def paintEvent(self, event):
271
+ """Render the game."""
272
+ painter = QPainter(self)
273
+ painter.setRenderHint(QPainter.RenderHint.Antialiasing)
274
+
275
+ # Background
276
+ painter.fillRect(self.rect(), self.bg_color)
277
+
278
+ # Stars
279
+ painter.setPen(QPen(self.star_color))
280
+ for star in self.stars:
281
+ painter.drawPoint(star[0], star[1])
282
+
283
+ if not self.game_running and not self.game_over:
284
+ self.draw_start_screen(painter)
285
+ return
286
+
287
+ # Draw player
288
+ painter.setBrush(QBrush(self.player_color))
289
+ painter.setPen(Qt.PenStyle.NoPen)
290
+ # Ship body
291
+ painter.drawRect(int(self.player_x), 565, self.player_width, 15)
292
+ # Ship cockpit
293
+ painter.drawRect(int(self.player_x) + 20, 555, 10, 10)
294
+
295
+ # Draw player bullets
296
+ painter.setBrush(QBrush(self.bullet_color))
297
+ for bullet in self.bullets:
298
+ painter.drawRect(int(bullet['x']), int(bullet['y']), 4, 10)
299
+
300
+ # Draw invaders
301
+ for inv in self.invaders:
302
+ if not inv['alive']:
303
+ continue
304
+ painter.setBrush(QBrush(inv['color']))
305
+ # Invader body
306
+ self.draw_invader(painter, int(inv['x']), int(inv['y']))
307
+
308
+ # Draw invader bullets
309
+ painter.setBrush(QBrush(QColor(255, 100, 100)))
310
+ for bullet in self.invader_bullets:
311
+ painter.drawRect(int(bullet['x']), int(bullet['y']), 4, 10)
312
+
313
+ # Draw UI
314
+ self.draw_ui(painter)
315
+
316
+ if self.game_over:
317
+ self.draw_game_over(painter)
318
+
319
+ def draw_invader(self, painter, x, y):
320
+ """Draw a pixelated invader."""
321
+ # Main body
322
+ painter.drawRect(x + 5, y + 5, 25, 15)
323
+ # Antennae
324
+ painter.drawRect(x + 2, y, 6, 8)
325
+ painter.drawRect(x + 27, y, 6, 8)
326
+ # Eyes
327
+ painter.setBrush(QBrush(QColor(0, 0, 0)))
328
+ painter.drawRect(x + 10, y + 8, 5, 5)
329
+ painter.drawRect(x + 20, y + 8, 5, 5)
330
+ # Restore color
331
+ painter.setBrush(QBrush(self.invader_colors[0]))
332
+
333
+ def draw_ui(self, painter):
334
+ """Draw score and lives."""
335
+ painter.setPen(QPen(QColor(255, 255, 255)))
336
+ font = QFont('Courier', 14, QFont.Weight.Bold)
337
+ painter.setFont(font)
338
+
339
+ # Score
340
+ painter.drawText(10, 25, f'SCORE: {self.score}')
341
+
342
+ # Level
343
+ painter.drawText(200, 25, f'LEVEL: {self.level}')
344
+
345
+ # Lives
346
+ painter.drawText(380, 25, f'LIVES: {"♥" * self.lives}')
347
+
348
+ def draw_start_screen(self, painter):
349
+ """Draw the start screen."""
350
+ painter.setPen(QPen(QColor(0, 255, 100)))
351
+
352
+ # Title
353
+ title_font = QFont('Courier', 32, QFont.Weight.Bold)
354
+ painter.setFont(title_font)
355
+ painter.drawText(self.rect(), Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop,
356
+ '\n\nSPACE INVADERS')
357
+
358
+ # SQLShell edition
359
+ sub_font = QFont('Courier', 14)
360
+ painter.setFont(sub_font)
361
+ painter.setPen(QPen(QColor(100, 200, 255)))
362
+ painter.drawText(self.rect(), Qt.AlignmentFlag.AlignHCenter,
363
+ '\n\n\n\n\n\nSQLShell Edition')
364
+
365
+ # Instructions
366
+ painter.setPen(QPen(QColor(255, 255, 255)))
367
+ inst_font = QFont('Courier', 12)
368
+ painter.setFont(inst_font)
369
+
370
+ instructions = [
371
+ '',
372
+ '',
373
+ '← → or A D: Move',
374
+ 'SPACE: Shoot',
375
+ '',
376
+ 'Press SPACE or click START to play!'
377
+ ]
378
+
379
+ y = 300
380
+ for line in instructions:
381
+ painter.drawText(self.rect().adjusted(0, y, 0, 0),
382
+ Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop, line)
383
+ y += 25
384
+
385
+ # Draw sample invaders
386
+ painter.setBrush(QBrush(self.invader_colors[0]))
387
+ self.draw_invader(painter, 150, 200)
388
+ painter.setBrush(QBrush(self.invader_colors[1]))
389
+ self.draw_invader(painter, 220, 200)
390
+ painter.setBrush(QBrush(self.invader_colors[2]))
391
+ self.draw_invader(painter, 290, 200)
392
+
393
+ def draw_game_over(self, painter):
394
+ """Draw game over screen."""
395
+ # Darken background
396
+ painter.fillRect(self.rect(), QColor(0, 0, 0, 150))
397
+
398
+ painter.setPen(QPen(QColor(255, 50, 50)))
399
+ font = QFont('Courier', 36, QFont.Weight.Bold)
400
+ painter.setFont(font)
401
+ painter.drawText(self.rect(), Qt.AlignmentFlag.AlignCenter, 'GAME OVER')
402
+
403
+ painter.setPen(QPen(QColor(255, 255, 255)))
404
+ score_font = QFont('Courier', 18)
405
+ painter.setFont(score_font)
406
+ painter.drawText(self.rect().adjusted(0, 60, 0, 0),
407
+ Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignCenter,
408
+ f'Final Score: {self.score}')
409
+
410
+ painter.setPen(QPen(QColor(100, 255, 100)))
411
+ painter.drawText(self.rect().adjusted(0, 120, 0, 0),
412
+ Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignCenter,
413
+ 'Press SPACE to play again')
414
+
415
+ def keyPressEvent(self, event: QKeyEvent):
416
+ """Handle key press."""
417
+ self.keys_pressed.add(event.key())
418
+
419
+ if event.key() == Qt.Key.Key_Space:
420
+ if not self.game_running:
421
+ self.start_game()
422
+
423
+ event.accept()
424
+
425
+ def keyReleaseEvent(self, event: QKeyEvent):
426
+ """Handle key release."""
427
+ self.keys_pressed.discard(event.key())
428
+ event.accept()
429
+
430
+
431
+ class SpaceInvadersDialog(QDialog):
432
+ """Dialog wrapper for the Space Invaders game."""
433
+
434
+ def __init__(self, parent=None):
435
+ super().__init__(parent)
436
+ self.setWindowTitle('🎮 Space Invaders - SQLShell Edition')
437
+ self.setMinimumSize(520, 700)
438
+ self.setModal(True)
439
+
440
+ # Dark theme
441
+ self.setStyleSheet("""
442
+ QDialog {
443
+ background-color: #0a0a1a;
444
+ }
445
+ QPushButton {
446
+ background-color: #1a3a1a;
447
+ color: #00ff64;
448
+ border: 2px solid #00ff64;
449
+ padding: 10px 20px;
450
+ font-family: 'Courier New', monospace;
451
+ font-size: 14px;
452
+ font-weight: bold;
453
+ }
454
+ QPushButton:hover {
455
+ background-color: #2a5a2a;
456
+ }
457
+ QPushButton:pressed {
458
+ background-color: #0a2a0a;
459
+ }
460
+ QLabel {
461
+ color: #00ff64;
462
+ font-family: 'Courier New', monospace;
463
+ }
464
+ """)
465
+
466
+ layout = QVBoxLayout(self)
467
+ layout.setContentsMargins(10, 10, 10, 10)
468
+
469
+ # Game widget
470
+ self.game = SpaceInvadersGame(self)
471
+ layout.addWidget(self.game)
472
+
473
+ # Start button
474
+ self.start_btn = QPushButton('▶ START GAME')
475
+ self.start_btn.clicked.connect(self.start_game)
476
+ layout.addWidget(self.start_btn)
477
+
478
+ # Close button
479
+ self.close_btn = QPushButton('✕ CLOSE')
480
+ self.close_btn.clicked.connect(self.close)
481
+ layout.addWidget(self.close_btn)
482
+
483
+ def start_game(self):
484
+ """Start the game."""
485
+ self.game.start_game()
486
+ self.start_btn.setText('▶ RESTART')
487
+
488
+ def keyPressEvent(self, event: QKeyEvent):
489
+ """Forward key events to game."""
490
+ self.game.keyPressEvent(event)
491
+
492
+ def keyReleaseEvent(self, event: QKeyEvent):
493
+ """Forward key events to game."""
494
+ self.game.keyReleaseEvent(event)
495
+
496
+
497
+ def show_space_invaders(parent=None):
498
+ """Show the Space Invaders game dialog."""
499
+ dialog = SpaceInvadersDialog(parent)
500
+ dialog.exec()
501
+