meccatronis commited on
Commit
f635a7b
·
verified ·
1 Parent(s): 9bf0f20

Upload gui/main_window.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. gui/main_window.py +474 -0
gui/main_window.py ADDED
@@ -0,0 +1,474 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Main Window Module
3
+ ==================
4
+
5
+ Main application window for Android Data Recovery.
6
+ """
7
+
8
+ import os
9
+ import logging
10
+ from PyQt6.QtWidgets import ( # type: ignore
11
+ QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTabWidget,
12
+ QStatusBar, QMenuBar, QMenu, QMessageBox, QFileDialog,
13
+ QSplitter, QFrame, QLabel
14
+ )
15
+ from PyQt6.QtCore import Qt, pyqtSignal, QSettings # type: ignore
16
+ from PyQt6.QtGui import QAction, QIcon, QFont # type: ignore
17
+
18
+ from .styles import DARK_THEME, LIGHT_THEME, get_icon
19
+ from .device_panel import DevicePanel
20
+ from .scan_panel import ScanPanel
21
+ from .recovery_panel import RecoveryPanel
22
+ from .preview_panel import PreviewPanel
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ class MainWindow(QMainWindow):
28
+ """Main application window."""
29
+
30
+ def __init__(self, adb_manager=None, parent=None):
31
+ super().__init__(parent)
32
+ self.adb_manager = adb_manager
33
+ self.current_theme = "dark"
34
+ self.settings = QSettings("AndroidDataRecovery", "MainWindow")
35
+
36
+ # Initialize UI
37
+ self._setup_ui()
38
+ self._setup_menu_bar()
39
+ self._setup_status_bar()
40
+ self._apply_theme()
41
+ self._restore_settings()
42
+
43
+ logger.info("Main window initialized")
44
+
45
+ def _setup_ui(self):
46
+ """Setup the user interface."""
47
+ self.setWindowTitle("Android Data Recovery")
48
+ self.setMinimumSize(1200, 800)
49
+
50
+ # Central widget
51
+ central_widget = QWidget()
52
+ self.setCentralWidget(central_widget)
53
+ main_layout = QVBoxLayout(central_widget)
54
+ main_layout.setContentsMargins(0, 0, 0, 0)
55
+ main_layout.setSpacing(0)
56
+
57
+ # Header
58
+ header = self._create_header()
59
+ main_layout.addWidget(header)
60
+
61
+ # Main content area with splitter
62
+ splitter = QSplitter(Qt.Orientation.Horizontal)
63
+
64
+ # Left panel - Device connection
65
+ self.device_panel = DevicePanel(self.adb_manager)
66
+ self.device_panel.setMaximumWidth(400)
67
+ self.device_panel.setMinimumWidth(300)
68
+ splitter.addWidget(self.device_panel)
69
+
70
+ # Right panel - Tab widget for different functions
71
+ self.tab_widget = QTabWidget()
72
+ self.tab_widget.setTabPosition(QTabWidget.TabPosition.North)
73
+
74
+ # Create panels
75
+ self.scan_panel = ScanPanel(self.adb_manager)
76
+ self.recovery_panel = RecoveryPanel(self.adb_manager)
77
+ self.preview_panel = PreviewPanel()
78
+
79
+ # Add panels to tab widget
80
+ self.tab_widget.addTab(self.scan_panel, f"{get_icon('scan')} Scan")
81
+ self.tab_widget.addTab(self.recovery_panel, f"{get_icon('recover')} Recuperação")
82
+ self.tab_widget.addTab(self.preview_panel, f"{get_icon('file')} Visualização")
83
+
84
+ splitter.addWidget(self.tab_widget)
85
+
86
+ # Set splitter proportions
87
+ splitter.setStretchFactor(0, 0)
88
+ splitter.setStretchFactor(1, 1)
89
+ splitter.setSizes([350, 850])
90
+
91
+ main_layout.addWidget(splitter)
92
+
93
+ # Connect device panel signals
94
+ self.device_panel.device_connected.connect(self._on_device_connected)
95
+ self.device_panel.device_disconnected.connect(self._on_device_disconnected)
96
+
97
+ # Connect scan panel signals
98
+ self.scan_panel.files_selected.connect(self._on_files_selected)
99
+
100
+ # Connect recovery panel signals
101
+ self.recovery_panel.recovery_finished.connect(self._on_recovery_finished)
102
+
103
+ def _create_header(self) -> QFrame:
104
+ """Create the application header."""
105
+ header = QFrame()
106
+ header.setObjectName("header")
107
+ header.setStyleSheet("""
108
+ QFrame#header {
109
+ background-color: #2d2d2d;
110
+ border-bottom: 2px solid #424242;
111
+ padding: 15px;
112
+ }
113
+ """)
114
+
115
+ layout = QHBoxLayout(header)
116
+ layout.setContentsMargins(20, 10, 20, 10)
117
+
118
+ # App icon and title
119
+ icon_label = QLabel(get_icon('android'))
120
+ icon_label.setFont(QFont('Segoe UI Emoji', 32))
121
+ layout.addWidget(icon_label)
122
+
123
+ title_layout = QVBoxLayout()
124
+ title_layout.setSpacing(5)
125
+
126
+ title_label = QLabel("Android Data Recovery")
127
+ title_label.setObjectName("appTitle")
128
+ title_label.setStyleSheet("""
129
+ QLabel#appTitle {
130
+ font-size: 24px;
131
+ font-weight: bold;
132
+ color: #4fc3f7;
133
+ }
134
+ """)
135
+ title_layout.addWidget(title_label)
136
+
137
+ subtitle_label = QLabel("Recuperação de dados para dispositivos Android")
138
+ subtitle_label.setObjectName("appSubtitle")
139
+ subtitle_label.setStyleSheet("""
140
+ QLabel#appSubtitle {
141
+ font-size: 12px;
142
+ color: #b0b0b0;
143
+ }
144
+ """)
145
+ title_layout.addWidget(subtitle_label)
146
+
147
+ layout.addLayout(title_layout)
148
+ layout.addStretch()
149
+
150
+ # Version info
151
+ version_label = QLabel("v1.0.0")
152
+ version_label.setStyleSheet("color: #757575; font-size: 11px;")
153
+ layout.addWidget(version_label)
154
+
155
+ return header
156
+
157
+ def _setup_menu_bar(self):
158
+ """Setup the menu bar."""
159
+ menubar = self.menuBar()
160
+
161
+ # File menu
162
+ file_menu = menubar.addMenu("&Arquivo")
163
+
164
+ # New scan action
165
+ new_scan_action = QAction(f"{get_icon('scan')} Novo Scan", self)
166
+ new_scan_action.setShortcut("Ctrl+N")
167
+ new_scan_action.triggered.connect(self._new_scan)
168
+ file_menu.addAction(new_scan_action)
169
+
170
+ file_menu.addSeparator()
171
+
172
+ # Open recovered data
173
+ open_action = QAction(f"{get_icon('folder')} Abrir Dados Recuperados", self)
174
+ open_action.setShortcut("Ctrl+O")
175
+ open_action.triggered.connect(self._open_recovered_data)
176
+ file_menu.addAction(open_action)
177
+
178
+ file_menu.addSeparator()
179
+
180
+ # Export action
181
+ export_action = QAction(f"{get_icon('export')} Exportar Relatório", self)
182
+ export_action.setShortcut("Ctrl+E")
183
+ export_action.triggered.connect(self._export_report)
184
+ file_menu.addAction(export_action)
185
+
186
+ file_menu.addSeparator()
187
+
188
+ # Exit action
189
+ exit_action = QAction(f"{get_icon('exit')} Sair", self)
190
+ exit_action.setShortcut("Ctrl+Q")
191
+ exit_action.triggered.connect(self.close)
192
+ file_menu.addAction(exit_action)
193
+
194
+ # View menu
195
+ view_menu = menubar.addMenu("&Visualizar")
196
+
197
+ # Theme submenu
198
+ theme_menu = view_menu.addMenu("Tema")
199
+
200
+ dark_theme_action = QAction("Escuro", self)
201
+ dark_theme_action.setCheckable(True)
202
+ dark_theme_action.setChecked(True)
203
+ dark_theme_action.triggered.connect(lambda: self._set_theme("dark"))
204
+ theme_menu.addAction(dark_theme_action)
205
+
206
+ light_theme_action = QAction("Claro", self)
207
+ light_theme_action.setCheckable(True)
208
+ light_theme_action.triggered.connect(lambda: self._set_theme("light"))
209
+ theme_menu.addAction(light_theme_action)
210
+
211
+ view_menu.addSeparator()
212
+
213
+ # Refresh action
214
+ refresh_action = QAction(f"{get_icon('refresh')} Atualizar", self)
215
+ refresh_action.setShortcut("F5")
216
+ refresh_action.triggered.connect(self._refresh)
217
+ view_menu.addAction(refresh_action)
218
+
219
+ # Tools menu
220
+ tools_menu = menubar.addMenu("&Ferramentas")
221
+
222
+ # ADB tools
223
+ adb_menu = tools_menu.addMenu("ADB")
224
+
225
+ start_server_action = QAction("Iniciar Servidor ADB", self)
226
+ start_server_action.triggered.connect(self._start_adb_server)
227
+ adb_menu.addAction(start_server_action)
228
+
229
+ stop_server_action = QAction("Parar Servidor ADB", self)
230
+ stop_server_action.triggered.connect(self._stop_adb_server)
231
+ adb_menu.addAction(stop_server_action)
232
+
233
+ restart_server_action = QAction("Reiniciar Servidor ADB", self)
234
+ restart_server_action.triggered.connect(self._restart_adb_server)
235
+ adb_menu.addAction(restart_server_action)
236
+
237
+ tools_menu.addSeparator()
238
+
239
+ # Settings action
240
+ settings_action = QAction(f"{get_icon('settings')} Configurações", self)
241
+ settings_action.setShortcut("Ctrl+,")
242
+ settings_action.triggered.connect(self._show_settings)
243
+ tools_menu.addAction(settings_action)
244
+
245
+ # Help menu
246
+ help_menu = menubar.addMenu("&Ajuda")
247
+
248
+ # Documentation
249
+ docs_action = QAction(f"{get_icon('help')} Documentação", self)
250
+ docs_action.setShortcut("F1")
251
+ docs_action.triggered.connect(self._show_documentation)
252
+ help_menu.addAction(docs_action)
253
+
254
+ help_menu.addSeparator()
255
+
256
+ # About
257
+ about_action = QAction(f"{get_icon('info')} Sobre", self)
258
+ about_action.triggered.connect(self._show_about)
259
+ help_menu.addAction(about_action)
260
+
261
+ def _setup_status_bar(self):
262
+ """Setup the status bar."""
263
+ self.status_bar = QStatusBar()
264
+ self.setStatusBar(self.status_bar)
265
+
266
+ # Status message
267
+ self.status_label = QLabel("Pronto")
268
+ self.status_bar.addWidget(self.status_label)
269
+
270
+ # Device status
271
+ self.device_status_label = QLabel("Nenhum dispositivo conectado")
272
+ self.status_bar.addPermanentWidget(self.device_status_label)
273
+
274
+ def _apply_theme(self):
275
+ """Apply the current theme."""
276
+ if self.current_theme == "dark":
277
+ self.setStyleSheet(DARK_THEME)
278
+ else:
279
+ self.setStyleSheet(LIGHT_THEME)
280
+
281
+ def _set_theme(self, theme: str):
282
+ """Set the application theme."""
283
+ self.current_theme = theme
284
+ self._apply_theme()
285
+ self.settings.setValue("theme", theme)
286
+ self.status_label.setText(f"Tema alterado para {theme}")
287
+
288
+ def _restore_settings(self):
289
+ """Restore window settings."""
290
+ # Restore theme
291
+ theme = self.settings.value("theme", "dark")
292
+ self._set_theme(theme)
293
+
294
+ # Restore window geometry
295
+ geometry = self.settings.value("geometry")
296
+ if geometry:
297
+ self.restoreGeometry(geometry)
298
+
299
+ # Restore window state
300
+ state = self.settings.value("windowState")
301
+ if state:
302
+ self.restoreState(state)
303
+
304
+ def closeEvent(self, event):
305
+ """Handle window close event."""
306
+ # Save settings
307
+ self.settings.setValue("geometry", self.saveGeometry())
308
+ self.settings.setValue("windowState", self.saveState())
309
+
310
+ # Stop ADB server if running
311
+ if self.adb_manager:
312
+ self.adb_manager.stop_server()
313
+
314
+ logger.info("Application closing")
315
+ event.accept()
316
+
317
+ def _on_device_connected(self, serial: str):
318
+ """Handle device connection."""
319
+ self.device_status_label.setText(f"Conectado: {serial}")
320
+ self.status_label.setText(f"Dispositivo conectado: {serial}")
321
+ logger.info(f"Device connected: {serial}")
322
+
323
+ def _on_device_disconnected(self):
324
+ """Handle device disconnection."""
325
+ self.device_status_label.setText("Nenhum dispositivo conectado")
326
+ self.status_label.setText("Dispositivo desconectado")
327
+ logger.info("Device disconnected")
328
+
329
+ def _on_files_selected(self, files):
330
+ """Handle files selected from scan panel."""
331
+ # Switch to recovery tab
332
+ self.tab_widget.setCurrentIndex(1)
333
+
334
+ # Set files to recover
335
+ self.recovery_panel.set_files_to_recover(files)
336
+
337
+ self.status_label.setText(f"{len(files)} arquivo(s) selecionado(s) para recuperação")
338
+ logger.info(f"Files selected for recovery: {len(files)}")
339
+
340
+ def _on_recovery_finished(self, result):
341
+ """Handle recovery completion."""
342
+ self.status_label.setText(
343
+ f"Recuperação concluída: {result.recovered_files}/{result.total_files} arquivos"
344
+ )
345
+ logger.info(f"Recovery finished: {result.recovered_files}/{result.total_files} files")
346
+
347
+ def _new_scan(self):
348
+ """Start a new scan."""
349
+ if not self.device_panel.is_connected():
350
+ QMessageBox.warning(
351
+ self,
352
+ "Aviso",
353
+ "Por favor, conecte um dispositivo primeiro."
354
+ )
355
+ return
356
+
357
+ # Switch to scan tab
358
+ self.tab_widget.setCurrentIndex(0)
359
+
360
+ # Trigger scan on scan panel
361
+ self.scan_panel._start_scan()
362
+
363
+ def _open_recovered_data(self):
364
+ """Open recovered data directory."""
365
+ default_dir = "./recovered_data"
366
+ if os.path.exists(default_dir):
367
+ os.startfile(default_dir) if os.name == 'nt' else os.system(f'xdg-open "{default_dir}"')
368
+ else:
369
+ QMessageBox.information(
370
+ self,
371
+ "Info",
372
+ "Nenhum dado recuperado encontrado."
373
+ )
374
+
375
+ def _export_report(self):
376
+ """Export recovery report."""
377
+ # TODO: Implement report export
378
+ QMessageBox.information(
379
+ self,
380
+ "Info",
381
+ "Funcionalidade de exportação será implementada."
382
+ )
383
+
384
+ def _refresh(self):
385
+ """Refresh the application."""
386
+ self.device_panel.refresh_devices()
387
+ self.status_label.setText("Atualizado")
388
+
389
+ def _start_adb_server(self):
390
+ """Start ADB server."""
391
+ if self.adb_manager and self.adb_manager.start_server():
392
+ self.status_label.setText("Servidor ADB iniciado")
393
+ QMessageBox.information(self, "Sucesso", "Servidor ADB iniciado com sucesso")
394
+ else:
395
+ QMessageBox.warning(self, "Erro", "Falha ao iniciar servidor ADB")
396
+
397
+ def _stop_adb_server(self):
398
+ """Stop ADB server."""
399
+ if self.adb_manager and self.adb_manager.stop_server():
400
+ self.status_label.setText("Servidor ADB parado")
401
+ QMessageBox.information(self, "Sucesso", "Servidor ADB parado com sucesso")
402
+ else:
403
+ QMessageBox.warning(self, "Erro", "Falha ao parar servidor ADB")
404
+
405
+ def _restart_adb_server(self):
406
+ """Restart ADB server."""
407
+ if self.adb_manager and self.adb_manager.restart_server():
408
+ self.status_label.setText("Servidor ADB reiniciado")
409
+ QMessageBox.information(self, "Sucesso", "Servidor ADB reiniciado com sucesso")
410
+ else:
411
+ QMessageBox.warning(self, "Erro", "Falha ao reiniciar servidor ADB")
412
+
413
+ def _show_settings(self):
414
+ """Show settings dialog."""
415
+ # TODO: Implement settings dialog
416
+ QMessageBox.information(
417
+ self,
418
+ "Info",
419
+ "Configurações serão implementadas em uma janela dedicada."
420
+ )
421
+
422
+ def _show_documentation(self):
423
+ """Show documentation."""
424
+ # TODO: Implement documentation viewer
425
+ QMessageBox.information(
426
+ self,
427
+ "Documentação",
428
+ "Android Data Recovery v1.0.0\n\n"
429
+ "Este software permite recuperar dados de dispositivos Android usando ADB.\n\n"
430
+ "Funcionalidades:\n"
431
+ "- Scan de arquivos existentes e deletados\n"
432
+ "- Recuperação de contatos, mensagens e chamadas\n"
433
+ "- Recuperação de mídia (fotos, vídeos, áudio)\n"
434
+ "- Recuperação de dados do WhatsApp\n\n"
435
+ "Requisitos:\n"
436
+ "- ADB instalado no sistema\n"
437
+ "- Depuração USB ativada no dispositivo\n"
438
+ "- Root para acesso completo aos dados"
439
+ )
440
+
441
+ def _show_about(self):
442
+ """Show about dialog."""
443
+ QMessageBox.about(
444
+ self,
445
+ "Sobre Android Data Recovery",
446
+ "<h3>Android Data Recovery</h3>"
447
+ "<p>Versão 1.0.0</p>"
448
+ "<p>Software de recuperação de dados para dispositivos Android.</p>"
449
+ "<p><b>Recursos:</b></p>"
450
+ "<ul>"
451
+ "<li>Scan de arquivos existentes e deletados</li>"
452
+ "<li>Recuperação de contatos, mensagens e chamadas</li>"
453
+ "<li>Recuperação de mídia (fotos, vídeos, áudio)</li>"
454
+ "<li>Recuperação de dados do WhatsApp</li>"
455
+ "</ul>"
456
+ "<p><b>Desenvolvido com:</b></p>"
457
+ "<ul>"
458
+ "<li>Python 3.8+</li>"
459
+ "<li>PyQt6</li>"
460
+ "<li>ADB (Android Debug Bridge)</li>"
461
+ "</ul>"
462
+ )
463
+
464
+ def add_tab(self, widget, title: str):
465
+ """Add a tab to the main tab widget."""
466
+ self.tab_widget.addTab(widget, title)
467
+
468
+ def get_current_tab(self):
469
+ """Get the currently active tab widget."""
470
+ return self.tab_widget.currentWidget()
471
+
472
+ def set_status_message(self, message: str):
473
+ """Set the status bar message."""
474
+ self.status_label.setText(message)