-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
276 lines (227 loc) · 12.2 KB
/
main.py
File metadata and controls
276 lines (227 loc) · 12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# Arquivo: main.py
import sys
import os
import json
from pathlib import Path
# --- CORREÇÃO DE PLATAFORMA PARA LINUX ---
if sys.platform == "linux":
os.environ['QT_QPA_PLATFORM'] = 'xcb'
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--disable-gpu --disable-software-rasterizer"
from PySide6.QtCore import QUrl, QLocale, Qt, QLibraryInfo, QTranslator
from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog
from PySide6.QtGui import QIcon
from PySide6.QtWebEngineCore import QWebEngineProfile
# Módulos do projeto
from panels import AIPanel
from components import WebView, HtmlEditor, UrlInterceptor, NetworkPanel, SplitEditor
from actions import FileMenuActions, BookmarkActions, FindActions, PrivacyActions, DownloadActions, HistoryActions, DevToolsActions, ProtocolActions, AIActions
from browser_core import UIBuilderMixin, TabManagerMixin, EventHandlersMixin
# --- CONSTANTES GLOBAIS ---
# Nomes de arquivos de configuração e dados
CONFIG_FILE_NAME = "navfep_config.json"
HISTORY_FILE_NAME = "navfep_history.json"
PROXIES_FILE = "proxies.json"
BOOKMARKS_FILE = "bookmarks.json"
# URLs e Títulos Padrão
PAGINA_INICIAL_URL = "https://f3l1p3.neocities.org"
PAGINA_INICIAL_TITULO = "Página Inicial"
def carregar_traducoes(app):
"""Carrega os arquivos de tradução do Qt para Português."""
caminho_traducoes = QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath)
qt_translator = QTranslator(app)
webengine_translator = QTranslator(app)
if qt_translator.load("qt_pt", caminho_traducoes):
app.installTranslator(qt_translator)
if webengine_translator.load("qtwebengine_pt", caminho_traducoes):
app.installTranslator(webengine_translator)
# --- CLASSE PRINCIPAL ---
class Navegador(QMainWindow, FileMenuActions, BookmarkActions, FindActions, PrivacyActions, DownloadActions, HistoryActions, DevToolsActions, ProtocolActions, AIActions, UIBuilderMixin, TabManagerMixin, EventHandlersMixin):
"""
Classe principal do navegador, que integra todos os componentes da UI e funcionalidades.
"""
def __init__(self):
super().__init__()
self._configurar_janela()
self._inicializar_variaveis_e_dados()
self._setup_ui()
self._carregar_dados_iniciais()
# Inicia o navegador com a página inicial
self.adicionar_nova_aba(QUrl(PAGINA_INICIAL_URL), PAGINA_INICIAL_TITULO)
# --- FUNÇÕES AUXILIARES ---
def get_data_path(self, filename):
"""Retorna o caminho completo para um arquivo de dados, funcionando tanto em modo script quanto em executável (PyInstaller)."""
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
base_path = sys._MEIPASS
else:
base_path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(base_path, filename)
def _configurar_janela(self):
"""Define as propriedades básicas da janela principal (título, tamanho, ícone)."""
self.setWindowTitle("Navegador do FEP")
self.setGeometry(100, 100, 1280, 800)
icon_path = self.get_data_path("navfep.png")
if os.path.exists(icon_path):
self.setWindowIcon(QIcon(icon_path))
def _inicializar_variaveis_e_dados(self):
"""Inicializa as variáveis de estado e estruturas de dados do navegador."""
self.default_user_agent = QWebEngineProfile.defaultProfile().httpUserAgent()
self.user_agents = {
"Chrome no Windows 11": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
"Edge no Windows 11": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0",
"Firefox no Windows 11": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Chrome no macOS (Apple Silicon)": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
"Safari no macOS (Apple Silicon)": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15",
"Chrome no Android (Pixel 8)": "Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36",
"Safari no iPhone (iOS 18)": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
"Chrome no Linux (Ubuntu)": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
"Firefox no Linux (Ubuntu)": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Googlebot (Robô do Google)": "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
}
self.current_ua_name = "UA Padrão"
self.api_key = ""
self.config_file = os.path.join(str(Path.home()), CONFIG_FILE_NAME)
self.proxy_list = []
self.current_proxy_index = -1
self.proxies_file = PROXIES_FILE
self.bookmarks = {}
self.bookmarks_file = BOOKMARKS_FILE
self.blocklist = set()
self.active_downloads = []
self.history = []
self.history_file = os.path.join(str(Path.home()), HISTORY_FILE_NAME)
def _carregar_dados_iniciais(self):
"""Carrega todas as configurações e dados salvos de arquivos."""
self.carregar_config()
self.carregar_historico()
self.carregar_proxies()
self.carregar_favoritos()
self.atualizar_barra_favoritos()
self.carregar_blocklist()
QWebEngineProfile.defaultProfile().downloadRequested.connect(self.handle_download_request)
def _executar_se_webview(self, acao_lambda):
"""
Executa uma ação na aba atual somente se ela for um WebView.
Responsabilidades:
- Verificar se a aba atual é uma instância de WebView.
- Caso seja, executar a ação passada como parâmetro (função lambda).
- Evita erros ao tentar aplicar ações de navegação em abas que não sejam páginas web
(por exemplo, editores de texto ou painéis internos).
:param acao_lambda: Função (lambda) que recebe a aba como argumento e executa a ação desejada.
"""
aba = self.aba_atual()
if isinstance(aba, WebView):
acao_lambda(aba)
def carregar_config(self):
"""
Carrega a configuração do navegador a partir de um arquivo JSON.
Responsabilidades:
- Ler o arquivo de configuração definido em `self.config_file`.
- Extrair a chave de API (api_key) usada pelo assistente de IA.
- Caso o arquivo não exista ou esteja corrompido, inicializa a chave como vazia.
Observação:
- O uso de `try/except` garante robustez contra erros de leitura ou parsing.
"""
try:
with open(self.config_file, "r", encoding='utf-8') as f:
config = json.load(f)
self.api_key = config.get("api_key", "")
except (FileNotFoundError, json.JSONDecodeError):
self.api_key = ""
def salvar_config(self):
"""
Salva a configuração atual do navegador em um arquivo JSON.
Responsabilidades:
- Persistir a chave de API (api_key) para uso futuro.
- Garantir que o arquivo seja gravado em UTF-8 e formatado com indentação
para facilitar leitura manual.
Observação:
- Esse método é chamado, por exemplo, quando o usuário insere uma nova chave de API.
"""
config = {"api_key": self.api_key}
with open(self.config_file, "w", encoding='utf-8') as f:
json.dump(config, f, indent=4)
def definir_user_agent_global(self, name, user_agent_string):
"""
Define o User-Agent global do navegador.
Responsabilidades:
- Atualizar o User-Agent padrão do perfil global (`QWebEngineProfile.defaultProfile`).
- Se nenhum User-Agent for fornecido, utiliza o padrão definido em `self.default_user_agent`.
- Atualizar o texto do botão de seleção de User-Agent para refletir a escolha atual.
- Recarregar a aba atual (se for um WebView) para aplicar o novo User-Agent.
:param name: Nome amigável do User-Agent (ex.: "Chrome", "Firefox", "UA Padrão").
:param user_agent_string: String completa do User-Agent a ser usada.
"""
final_ua = user_agent_string or self.default_user_agent
QWebEngineProfile.defaultProfile().setHttpUserAgent(final_ua)
# Atualiza o estado interno e a interface
self.current_ua_name = name
self.ua_button.setText(self.current_ua_name)
# Recarrega a aba atual para aplicar o novo User-Agent
self._executar_se_webview(lambda aba: aba.reload())
def toggle_full_screen(self, checked):
"""
Alterna entre o modo de tela cheia e o modo normal da janela.
Responsabilidades:
- Se `checked` for True, coloca a janela em tela cheia.
- Se `checked` for False, restaura para o modo normal.
:param checked: Booleano indicando o estado desejado (True = tela cheia).
"""
self.showFullScreen() if checked else self.showNormal()
def keyPressEvent(self, event):
"""
Sobrescreve o evento de tecla pressionada para tratar atalhos especiais.
Responsabilidades:
- Se a tecla pressionada for ESC e a janela estiver em tela cheia:
- Desmarca a ação de tela cheia (`fullscreen_action`), retornando ao modo normal.
- Caso contrário, delega o tratamento ao comportamento padrão da superclasse.
"""
if event.key() == Qt.Key_Escape and self.isFullScreen():
if hasattr(self, 'fullscreen_action'):
self.fullscreen_action.setChecked(False)
else:
super().keyPressEvent(event)
def abrir_pdf_dialogo(self):
"""
Abre um diálogo para seleção de arquivo PDF e o exibe em uma nova aba.
Responsabilidades:
- Exibir um diálogo de seleção de arquivo filtrado para PDFs.
- Se o usuário selecionar um arquivo válido:
- Cria uma nova aba com o visualizador de PDF.
- Carrega o arquivo selecionado diretamente no WebView.
"""
caminho, _ = QFileDialog.getOpenFileName(self, "Abrir PDF", "", "Arquivos PDF (*.pdf)")
if caminho:
self.adicionar_nova_aba(QUrl.fromLocalFile(caminho), "Visualizador PDF")
def preview_html(self):
"""
Renderiza a pré-visualização do HTML no editor lado a lado (SplitEditor).
- Obtém a aba atual.
- Se for um SplitEditor, chama `render_preview()` para atualizar a visualização.
"""
aba_atual = self.aba_atual()
if isinstance(aba_atual, SplitEditor):
aba_atual.render_preview()
if __name__ == "__main__":
"""
Ponto de entrada principal da aplicação.
Responsabilidades:
- Criar a instância da aplicação Qt (`QApplication`).
- Carregar traduções (se a função `carregar_traducoes` estiver disponível).
- Definir a localidade padrão como português do Brasil (`pt_BR`).
- Criar e exibir a janela principal do navegador (`Navegador`).
- Executar o loop de eventos da aplicação até o encerramento.
"""
# Cria a aplicação Qt
app = QApplication(sys.argv)
# Tenta carregar traduções, se a função estiver definida
try:
carregar_traducoes(app)
except NameError:
pass # Ignora se a função não existir
# Define a localidade padrão como português do Brasil
QLocale.setDefault(QLocale("pt_BR"))
# Cria a janela principal do navegador
window = Navegador()
window.showMaximized() # Exibe a janela em modo maximizado
# Inicia o loop de eventos da aplicação
sys.exit(app.exec())