article-introduction-generator 0.1.0__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.
- article_introduction_generator/__init__.py +1 -0
- article_introduction_generator/about.py +12 -0
- article_introduction_generator/desktop.py +103 -0
- article_introduction_generator/icons/logo.png +0 -0
- article_introduction_generator/modules/__init__.py +1 -0
- article_introduction_generator/modules/configure.py +59 -0
- article_introduction_generator/modules/consult.py +72 -0
- article_introduction_generator/modules/wabout.py +108 -0
- article_introduction_generator/program.py +993 -0
- article_introduction_generator/program_old.py +624 -0
- article_introduction_generator-0.1.0.dist-info/METADATA +59 -0
- article_introduction_generator-0.1.0.dist-info/RECORD +16 -0
- article_introduction_generator-0.1.0.dist-info/WHEEL +5 -0
- article_introduction_generator-0.1.0.dist-info/entry_points.txt +2 -0
- article_introduction_generator-0.1.0.dist-info/licenses/LICENSE +674 -0
- article_introduction_generator-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,993 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import signal
|
|
6
|
+
import traceback
|
|
7
|
+
|
|
8
|
+
from PyQt5.QtWidgets import (
|
|
9
|
+
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
|
|
10
|
+
QLabel, QTextEdit, QLineEdit, QPushButton, QFileDialog,
|
|
11
|
+
QTabWidget, QListWidget, QMessageBox, QStatusBar, QToolBar,
|
|
12
|
+
QComboBox, QScrollArea, QListWidgetItem, QSizePolicy, QAction, QDialog
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from PyQt5.QtGui import QIcon, QDesktopServices
|
|
16
|
+
from PyQt5.QtCore import Qt, QUrl, QSize
|
|
17
|
+
from PyQt5.QtCore import QObject, QThread, pyqtSignal
|
|
18
|
+
|
|
19
|
+
import article_introduction_generator.about as about
|
|
20
|
+
import article_introduction_generator.modules.configure as configure
|
|
21
|
+
from article_introduction_generator.modules.wabout import show_about_window
|
|
22
|
+
from article_introduction_generator.desktop import create_desktop_file, create_desktop_directory, create_desktop_menu
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
from article_introduction_generator.modules.consult import consultation_in_depth
|
|
26
|
+
|
|
27
|
+
# ---------- Path to config file ----------
|
|
28
|
+
CONFIG_PATH = os.path.join( os.path.expanduser("~"),
|
|
29
|
+
".config",
|
|
30
|
+
about.__package__,
|
|
31
|
+
"config.json" )
|
|
32
|
+
|
|
33
|
+
DEFAULT_CONTENT={ "toolbar_llm_conf": "LLM Conf.",
|
|
34
|
+
"toolbar_llm_conf_tooltip": "Open the configure Json file of LLM",
|
|
35
|
+
"toolbar_url_usage": "LLM Usage",
|
|
36
|
+
"toolbar_url_usage_tooltip": "Open the web page that shows the data usage and cost.",
|
|
37
|
+
"toolbar_configure": "Configure",
|
|
38
|
+
"toolbar_configure_tooltip": "Open the configure Json file of program GUI",
|
|
39
|
+
"toolbar_about": "About",
|
|
40
|
+
"toolbar_about_tooltip": "About the program",
|
|
41
|
+
"toolbar_coffee": "Coffee",
|
|
42
|
+
"toolbar_coffee_tooltip": "Buy me a coffee (TrucomanX)",
|
|
43
|
+
"window_width": 1024,
|
|
44
|
+
"window_height": 800
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
configure.verify_default_config(CONFIG_PATH,default_content=DEFAULT_CONTENT)
|
|
48
|
+
|
|
49
|
+
CONFIG=configure.load_config(CONFIG_PATH)
|
|
50
|
+
|
|
51
|
+
# ---------- Path to config LLM file ----------
|
|
52
|
+
CONFIG_LLM_PATH = os.path.join( os.path.expanduser("~"),
|
|
53
|
+
".config",
|
|
54
|
+
about.__package__,
|
|
55
|
+
"config.llm.json" )
|
|
56
|
+
|
|
57
|
+
DEFAULT_LLM_CONTENT={
|
|
58
|
+
"api_key": "",
|
|
59
|
+
"usage": "https://deepinfra.com/dash/usage",
|
|
60
|
+
"base_url": "https://api.deepinfra.com/v1/openai",
|
|
61
|
+
"model": "meta-llama/Meta-Llama-3.1-70B-Instruct"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
configure.verify_default_config(CONFIG_LLM_PATH,default_content=DEFAULT_LLM_CONTENT)
|
|
65
|
+
|
|
66
|
+
CONFIG_LLM = configure.load_config(CONFIG_LLM_PATH)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# -------- Worker --------
|
|
70
|
+
class ConsultationWorker(QObject):
|
|
71
|
+
finished = pyqtSignal(str)
|
|
72
|
+
error = pyqtSignal(str)
|
|
73
|
+
|
|
74
|
+
def __init__(self, config, data):
|
|
75
|
+
super().__init__()
|
|
76
|
+
self.config = config
|
|
77
|
+
self.data = data
|
|
78
|
+
|
|
79
|
+
def run(self):
|
|
80
|
+
try:
|
|
81
|
+
result = consultation_in_depth(self.config, self.data)
|
|
82
|
+
self.finished.emit(result)
|
|
83
|
+
except Exception as e:
|
|
84
|
+
self.error.emit(str(e))
|
|
85
|
+
|
|
86
|
+
# -------- Error dialog --------
|
|
87
|
+
class MessageDialog(QDialog):
|
|
88
|
+
"""Error dialog with scrollable text area"""
|
|
89
|
+
def __init__( self,
|
|
90
|
+
message,
|
|
91
|
+
parent=None,
|
|
92
|
+
window_title = "Title message",
|
|
93
|
+
title_message = "Some text",
|
|
94
|
+
button_ok_text = "OK",
|
|
95
|
+
button_copy_text = "Copy to clipboard",
|
|
96
|
+
width = 400,
|
|
97
|
+
height = 300
|
|
98
|
+
):
|
|
99
|
+
|
|
100
|
+
super().__init__(parent)
|
|
101
|
+
|
|
102
|
+
self.setWindowTitle(window_title)
|
|
103
|
+
self.resize(width, height)
|
|
104
|
+
|
|
105
|
+
# Layout principal
|
|
106
|
+
layout = QVBoxLayout(self)
|
|
107
|
+
|
|
108
|
+
# Label
|
|
109
|
+
label = QLabel(title_message)
|
|
110
|
+
layout.addWidget(label)
|
|
111
|
+
|
|
112
|
+
# Text area
|
|
113
|
+
self.text_edit = QTextEdit()
|
|
114
|
+
self.text_edit.setPlainText(message)
|
|
115
|
+
self.text_edit.setReadOnly(True)
|
|
116
|
+
self.text_edit.setLineWrapMode(QTextEdit.WidgetWidth)
|
|
117
|
+
layout.addWidget(self.text_edit)
|
|
118
|
+
|
|
119
|
+
# Layout dos botões
|
|
120
|
+
button_layout = QHBoxLayout()
|
|
121
|
+
|
|
122
|
+
# Botão copiar
|
|
123
|
+
copy_button = QPushButton(button_copy_text)
|
|
124
|
+
copy_button.clicked.connect(self.copy_to_clipboard)
|
|
125
|
+
button_layout.addWidget(copy_button)
|
|
126
|
+
|
|
127
|
+
# Botão OK
|
|
128
|
+
ok_button = QPushButton(button_ok_text)
|
|
129
|
+
ok_button.clicked.connect(self.accept)
|
|
130
|
+
button_layout.addWidget(ok_button)
|
|
131
|
+
|
|
132
|
+
layout.addLayout(button_layout)
|
|
133
|
+
|
|
134
|
+
def copy_to_clipboard(self):
|
|
135
|
+
clipboard = QApplication.clipboard()
|
|
136
|
+
clipboard.setText(self.text_edit.toPlainText())
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def show_error_dialog( message,
|
|
140
|
+
title_message = "An error occurred:",
|
|
141
|
+
width = 800,
|
|
142
|
+
height = 600 ):
|
|
143
|
+
dialog = MessageDialog( message,
|
|
144
|
+
window_title = "Error message",
|
|
145
|
+
title_message = title_message,
|
|
146
|
+
button_ok_text = "OK",
|
|
147
|
+
button_copy_text = "Copy to clipboard",
|
|
148
|
+
width = width,
|
|
149
|
+
height = height
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
dialog.exec_()
|
|
153
|
+
|
|
154
|
+
def show_info_dialog( message,
|
|
155
|
+
title_message = "",
|
|
156
|
+
width = 800,
|
|
157
|
+
height = 600 ):
|
|
158
|
+
dialog = MessageDialog( message,
|
|
159
|
+
window_title = "Information message",
|
|
160
|
+
title_message = title_message,
|
|
161
|
+
button_ok_text = "OK",
|
|
162
|
+
button_copy_text = "Copy to clipboard",
|
|
163
|
+
width = width,
|
|
164
|
+
height = height
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
dialog.exec_()
|
|
168
|
+
|
|
169
|
+
# ---------- Reusable Widgets ----------
|
|
170
|
+
|
|
171
|
+
class LabeledTextEdit(QWidget):
|
|
172
|
+
def __init__(self, label, tooltip=""):
|
|
173
|
+
super().__init__()
|
|
174
|
+
layout = QHBoxLayout()
|
|
175
|
+
self.label = QLabel(label)
|
|
176
|
+
self.text = QTextEdit()
|
|
177
|
+
self.text.setToolTip(tooltip)
|
|
178
|
+
self.text.setMinimumHeight(80)
|
|
179
|
+
layout.addWidget(self.label, 1)
|
|
180
|
+
layout.addWidget(self.text, 4)
|
|
181
|
+
self.setLayout(layout)
|
|
182
|
+
|
|
183
|
+
def get(self):
|
|
184
|
+
return self.text.toPlainText().strip()
|
|
185
|
+
|
|
186
|
+
def set(self, value):
|
|
187
|
+
self.text.setPlainText(value or "")
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class LabeledLineEdit(QWidget):
|
|
191
|
+
def __init__(self, label, tooltip=""):
|
|
192
|
+
super().__init__()
|
|
193
|
+
layout = QHBoxLayout()
|
|
194
|
+
self.label = QLabel(label)
|
|
195
|
+
self.line = QLineEdit()
|
|
196
|
+
self.line.setToolTip(tooltip)
|
|
197
|
+
layout.addWidget(self.label, 1)
|
|
198
|
+
layout.addWidget(self.line, 4)
|
|
199
|
+
self.setLayout(layout)
|
|
200
|
+
|
|
201
|
+
def get(self):
|
|
202
|
+
return self.line.text().strip()
|
|
203
|
+
|
|
204
|
+
def set(self, value):
|
|
205
|
+
self.line.setText(value or "")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class StringListEditor(QWidget):
|
|
209
|
+
def __init__(self, label, tooltip=""):
|
|
210
|
+
super().__init__()
|
|
211
|
+
layout = QVBoxLayout()
|
|
212
|
+
title = QLabel(label)
|
|
213
|
+
title.setToolTip(tooltip)
|
|
214
|
+
self.list = QListWidget()
|
|
215
|
+
|
|
216
|
+
placeholder = QListWidgetItem("Click 'Add' to insert a new entry")
|
|
217
|
+
placeholder.setFlags(Qt.NoItemFlags) # não selecionável / não editável
|
|
218
|
+
placeholder.setForeground(Qt.gray)
|
|
219
|
+
self.list.addItem(placeholder)
|
|
220
|
+
|
|
221
|
+
self.list.setToolTip(tooltip)
|
|
222
|
+
|
|
223
|
+
btn_layout = QHBoxLayout()
|
|
224
|
+
add_btn = QPushButton("Add")
|
|
225
|
+
add_btn.setIcon(QIcon.fromTheme("list-add"))
|
|
226
|
+
remove_btn = QPushButton("Remove")
|
|
227
|
+
remove_btn.setIcon(QIcon.fromTheme("list-remove"))
|
|
228
|
+
btn_layout.addWidget(add_btn)
|
|
229
|
+
btn_layout.addWidget(remove_btn)
|
|
230
|
+
|
|
231
|
+
add_btn.clicked.connect(self.add_item)
|
|
232
|
+
remove_btn.clicked.connect(self.remove_item)
|
|
233
|
+
|
|
234
|
+
layout.addWidget(title)
|
|
235
|
+
layout.addWidget(self.list)
|
|
236
|
+
layout.addLayout(btn_layout)
|
|
237
|
+
self.setLayout(layout)
|
|
238
|
+
|
|
239
|
+
self.list.setEditTriggers(
|
|
240
|
+
self.list.DoubleClicked | self.list.SelectedClicked
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
def add_item(self, text=None):
|
|
244
|
+
from PyQt5.QtWidgets import QListWidgetItem
|
|
245
|
+
|
|
246
|
+
# Se o primeiro item for placeholder, remove
|
|
247
|
+
if self.list.count() == 1:
|
|
248
|
+
item0 = self.list.item(0)
|
|
249
|
+
if item0.flags() == Qt.NoItemFlags:
|
|
250
|
+
self.list.clear()
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
if not isinstance(text, str):
|
|
254
|
+
text = ""
|
|
255
|
+
|
|
256
|
+
item = QListWidgetItem(text)
|
|
257
|
+
item.setFlags(item.flags() | Qt.ItemIsEditable)
|
|
258
|
+
self.list.addItem(item)
|
|
259
|
+
self.list.editItem(item)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def remove_item(self):
|
|
263
|
+
for item in self.list.selectedItems():
|
|
264
|
+
self.list.takeItem(self.list.row(item))
|
|
265
|
+
|
|
266
|
+
if self.list.count() == 0:
|
|
267
|
+
self._add_placeholder()
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def get(self):
|
|
271
|
+
values = []
|
|
272
|
+
for i in range(self.list.count()):
|
|
273
|
+
item = self.list.item(i)
|
|
274
|
+
if not (item.flags() & Qt.ItemIsEditable):
|
|
275
|
+
continue
|
|
276
|
+
text = item.text().strip()
|
|
277
|
+
if text:
|
|
278
|
+
values.append(text)
|
|
279
|
+
return values
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def set(self, values):
|
|
283
|
+
self.list.clear()
|
|
284
|
+
|
|
285
|
+
if not values:
|
|
286
|
+
self._add_placeholder()
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
for v in values:
|
|
290
|
+
item = QListWidgetItem(v)
|
|
291
|
+
item.setFlags(item.flags() | Qt.ItemIsEditable)
|
|
292
|
+
self.list.addItem(item)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _add_placeholder(self):
|
|
296
|
+
placeholder = QListWidgetItem("Click 'Add' to insert a new entry")
|
|
297
|
+
placeholder.setFlags(Qt.NoItemFlags)
|
|
298
|
+
placeholder.setForeground(Qt.gray)
|
|
299
|
+
self.list.addItem(placeholder)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
# ---------- Main Window ----------
|
|
304
|
+
|
|
305
|
+
class JsonIntroductionEditor(QMainWindow):
|
|
306
|
+
def __init__(self):
|
|
307
|
+
super().__init__()
|
|
308
|
+
|
|
309
|
+
self.setWindowTitle(about.__program_name__)
|
|
310
|
+
#self.setGeometry(100, 100, 800, 240)
|
|
311
|
+
self.resize(CONFIG["window_width"], CONFIG["window_height"])
|
|
312
|
+
|
|
313
|
+
## Icon
|
|
314
|
+
# Get base directory for icons
|
|
315
|
+
base_dir_path = os.path.dirname(os.path.abspath(__file__))
|
|
316
|
+
self.icon_path = os.path.join(base_dir_path, 'icons', 'logo.png')
|
|
317
|
+
self.setWindowIcon(QIcon(self.icon_path))
|
|
318
|
+
|
|
319
|
+
self.current_path = None
|
|
320
|
+
|
|
321
|
+
self.references_data = {}
|
|
322
|
+
|
|
323
|
+
self.current_reference_key = None
|
|
324
|
+
|
|
325
|
+
self.tabs = QTabWidget()
|
|
326
|
+
self.setCentralWidget(self.tabs)
|
|
327
|
+
|
|
328
|
+
self._create_toolbar()
|
|
329
|
+
self._create_status_bar()
|
|
330
|
+
|
|
331
|
+
self._create_tabs()
|
|
332
|
+
self._apply_styles()
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
# ---------- UI ----------
|
|
336
|
+
def _apply_styles(self):
|
|
337
|
+
self.setStyleSheet("""
|
|
338
|
+
QListWidget {
|
|
339
|
+
border: 1px solid #999;
|
|
340
|
+
border-radius: 4px;
|
|
341
|
+
background-color: #f9f9f9;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
QListWidget::item {
|
|
345
|
+
padding: 4px;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
QListWidget::item:selected {
|
|
349
|
+
background-color: #cce5ff;
|
|
350
|
+
color: black;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
QTextEdit {
|
|
354
|
+
border: 1px solid #bbb;
|
|
355
|
+
border-radius: 4px;
|
|
356
|
+
background-color: #ffffff;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
QLineEdit {
|
|
360
|
+
border: 1px solid #bbb;
|
|
361
|
+
border-radius: 4px;
|
|
362
|
+
background-color: #ffffff;
|
|
363
|
+
padding: 2px;
|
|
364
|
+
}
|
|
365
|
+
QListWidget:empty {
|
|
366
|
+
background-color: #f9f9f9;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
QListWidget:empty::item {
|
|
370
|
+
color: #999;
|
|
371
|
+
}
|
|
372
|
+
""")
|
|
373
|
+
|
|
374
|
+
def _create_toolbar(self):
|
|
375
|
+
self.toolbar = self.addToolBar("Main")
|
|
376
|
+
self.toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
|
|
377
|
+
|
|
378
|
+
#
|
|
379
|
+
self.load_action = QAction(QIcon.fromTheme("document-open"), "Load JSON", self)
|
|
380
|
+
self.load_action.setToolTip("load JSON")
|
|
381
|
+
self.load_action.triggered.connect(self.load_json)
|
|
382
|
+
self.toolbar.addAction(self.load_action)
|
|
383
|
+
|
|
384
|
+
#
|
|
385
|
+
self.save_as_action = QAction(QIcon.fromTheme("document-save-as"), "Save as JSON", self)
|
|
386
|
+
self.save_as_action.setToolTip("save as")
|
|
387
|
+
self.save_as_action.triggered.connect(self.save_as_json)
|
|
388
|
+
self.toolbar.addAction(self.save_as_action)
|
|
389
|
+
|
|
390
|
+
#
|
|
391
|
+
self.generate_intro_action = QAction(QIcon.fromTheme("emblem-generic"), "Generate intro.", self)
|
|
392
|
+
self.generate_intro_action.setToolTip("Generate introduction")
|
|
393
|
+
self.generate_intro_action.triggered.connect(self.generate_intro)
|
|
394
|
+
self.toolbar.addAction(self.generate_intro_action)
|
|
395
|
+
|
|
396
|
+
# Adicionar o espaçador
|
|
397
|
+
spacer = QWidget()
|
|
398
|
+
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
|
|
399
|
+
self.toolbar.addWidget(spacer)
|
|
400
|
+
|
|
401
|
+
#
|
|
402
|
+
self.llm_conf_action = QAction(QIcon.fromTheme("document-properties"), CONFIG["toolbar_llm_conf"], self)
|
|
403
|
+
self.llm_conf_action.setToolTip(CONFIG["toolbar_llm_conf_tooltip"])
|
|
404
|
+
self.llm_conf_action.triggered.connect(self.open_llm_conf_editor)
|
|
405
|
+
self.toolbar.addAction(self.llm_conf_action)
|
|
406
|
+
|
|
407
|
+
#
|
|
408
|
+
self.url_usage_action = QAction(QIcon.fromTheme("emblem-web"), CONFIG["toolbar_url_usage"], self)
|
|
409
|
+
self.url_usage_action.setToolTip(CONFIG["toolbar_url_usage_tooltip"])
|
|
410
|
+
self.url_usage_action.triggered.connect(self.open_url_usage_editor)
|
|
411
|
+
self.toolbar.addAction(self.url_usage_action)
|
|
412
|
+
|
|
413
|
+
#
|
|
414
|
+
self.configure_action = QAction(QIcon.fromTheme("document-properties"), CONFIG["toolbar_configure"], self)
|
|
415
|
+
self.configure_action.setToolTip(CONFIG["toolbar_configure_tooltip"])
|
|
416
|
+
self.configure_action.triggered.connect(self.open_configure_editor)
|
|
417
|
+
self.toolbar.addAction(self.configure_action)
|
|
418
|
+
|
|
419
|
+
#
|
|
420
|
+
self.about_action = QAction(QIcon.fromTheme("help-about"), CONFIG["toolbar_about"], self)
|
|
421
|
+
self.about_action.setToolTip(CONFIG["toolbar_about_tooltip"])
|
|
422
|
+
self.about_action.triggered.connect(self.open_about)
|
|
423
|
+
self.toolbar.addAction(self.about_action)
|
|
424
|
+
|
|
425
|
+
# Coffee
|
|
426
|
+
self.coffee_action = QAction(QIcon.fromTheme("emblem-favorite"), CONFIG["toolbar_coffee"], self)
|
|
427
|
+
self.coffee_action.setToolTip(CONFIG["toolbar_coffee_tooltip"])
|
|
428
|
+
self.coffee_action.triggered.connect(self.on_coffee_action_click)
|
|
429
|
+
self.toolbar.addAction(self.coffee_action)
|
|
430
|
+
|
|
431
|
+
def _open_file_in_text_editor(self, filepath):
|
|
432
|
+
if os.name == 'nt': # Windows
|
|
433
|
+
os.startfile(filepath)
|
|
434
|
+
elif os.name == 'posix': # Linux/macOS
|
|
435
|
+
subprocess.run(['xdg-open', filepath])
|
|
436
|
+
|
|
437
|
+
def open_url_usage_editor(self):
|
|
438
|
+
QDesktopServices.openUrl(QUrl(CONFIG_LLM["usage"]))
|
|
439
|
+
|
|
440
|
+
def open_configure_editor(self):
|
|
441
|
+
self._open_file_in_text_editor(CONFIG_PATH)
|
|
442
|
+
|
|
443
|
+
def open_llm_conf_editor(self):
|
|
444
|
+
self._open_file_in_text_editor(CONFIG_LLM_PATH)
|
|
445
|
+
|
|
446
|
+
def open_about(self):
|
|
447
|
+
data={
|
|
448
|
+
"version": about.__version__,
|
|
449
|
+
"package": about.__package__,
|
|
450
|
+
"program_name": about.__program_name__,
|
|
451
|
+
"author": about.__author__,
|
|
452
|
+
"email": about.__email__,
|
|
453
|
+
"description": about.__description__,
|
|
454
|
+
"url_source": about.__url_source__,
|
|
455
|
+
"url_doc": about.__url_doc__,
|
|
456
|
+
"url_funding": about.__url_funding__,
|
|
457
|
+
"url_bugs": about.__url_bugs__
|
|
458
|
+
}
|
|
459
|
+
show_about_window(data,self.icon_path)
|
|
460
|
+
|
|
461
|
+
def on_coffee_action_click(self):
|
|
462
|
+
QDesktopServices.openUrl(QUrl("https://ko-fi.com/trucomanx"))
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def _create_status_bar(self):
|
|
466
|
+
self.status = QStatusBar()
|
|
467
|
+
self.setStatusBar(self.status)
|
|
468
|
+
|
|
469
|
+
def _wrap_scroll(self, widget):
|
|
470
|
+
scroll = QScrollArea()
|
|
471
|
+
scroll.setWidgetResizable(True)
|
|
472
|
+
scroll.setWidget(widget)
|
|
473
|
+
return scroll
|
|
474
|
+
|
|
475
|
+
def _create_tabs(self):
|
|
476
|
+
self.tabs.addTab(self._wrap_scroll(self._paper_profile_tab()), "Paper Profile")
|
|
477
|
+
self.tabs.addTab(self._wrap_scroll(self._research_problem_tab()), "Research Problem")
|
|
478
|
+
self.tabs.addTab(self._wrap_scroll(self._contributions_tab()), "Contributions")
|
|
479
|
+
self.tabs.addTab(self._related_work_tab(), "Related Work")
|
|
480
|
+
self.tabs.addTab(self._wrap_scroll(self._writing_guidelines_tab()), "Writing Guidelines")
|
|
481
|
+
|
|
482
|
+
self.tabs.currentChanged.connect(self._on_tab_changed)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
# ---------- Tabs ----------
|
|
486
|
+
|
|
487
|
+
def _on_tab_changed(self, index):
|
|
488
|
+
self._save_current_reference()
|
|
489
|
+
|
|
490
|
+
def _paper_profile_tab(self):
|
|
491
|
+
w = QWidget()
|
|
492
|
+
layout = QVBoxLayout()
|
|
493
|
+
|
|
494
|
+
self.pp_title = LabeledLineEdit(
|
|
495
|
+
"Title",
|
|
496
|
+
"Full paper title."
|
|
497
|
+
)
|
|
498
|
+
self.pp_domain = LabeledLineEdit(
|
|
499
|
+
"Domain",
|
|
500
|
+
"Research domain, e.g., Computer Vision, NLP, Systems."
|
|
501
|
+
)
|
|
502
|
+
self.pp_journal = LabeledLineEdit(
|
|
503
|
+
"Target Journal",
|
|
504
|
+
"Intended journal (IEEE, Elsevier, ACM, etc.)."
|
|
505
|
+
)
|
|
506
|
+
self.pp_keywords = StringListEditor(
|
|
507
|
+
"Keywords",
|
|
508
|
+
"High-level keywords describing the paper."
|
|
509
|
+
)
|
|
510
|
+
self.pp_summary = LabeledTextEdit(
|
|
511
|
+
"Author Intended Summary",
|
|
512
|
+
"Human-written summary describing what the paper does and why."
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
for wdg in [self.pp_title, self.pp_domain, self.pp_journal, self.pp_keywords, self.pp_summary]:
|
|
516
|
+
layout.addWidget(wdg)
|
|
517
|
+
|
|
518
|
+
w.setLayout(layout)
|
|
519
|
+
return w
|
|
520
|
+
|
|
521
|
+
def _research_problem_tab(self):
|
|
522
|
+
w = QWidget()
|
|
523
|
+
layout = QVBoxLayout()
|
|
524
|
+
|
|
525
|
+
self.rp_overview = LabeledTextEdit(
|
|
526
|
+
"Research Domain Overview",
|
|
527
|
+
"General overview of the research domain and its importance."
|
|
528
|
+
)
|
|
529
|
+
self.rp_specific = LabeledTextEdit(
|
|
530
|
+
"Specific Problem",
|
|
531
|
+
"Precise formulation of the problem addressed."
|
|
532
|
+
)
|
|
533
|
+
self.rp_challenges = StringListEditor(
|
|
534
|
+
"Practical Challenges",
|
|
535
|
+
"Key practical or theoretical challenges."
|
|
536
|
+
)
|
|
537
|
+
self.rp_insufficient = LabeledTextEdit(
|
|
538
|
+
"Why Existing Solutions Are Insufficient",
|
|
539
|
+
"High-level human assessment without citing specific papers."
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
for wdg in [self.rp_overview, self.rp_specific, self.rp_challenges, self.rp_insufficient]:
|
|
543
|
+
layout.addWidget(wdg)
|
|
544
|
+
|
|
545
|
+
w.setLayout(layout)
|
|
546
|
+
return w
|
|
547
|
+
|
|
548
|
+
def _contributions_tab(self):
|
|
549
|
+
w = QWidget()
|
|
550
|
+
layout = QVBoxLayout()
|
|
551
|
+
|
|
552
|
+
self.contributions = StringListEditor(
|
|
553
|
+
"Contributions",
|
|
554
|
+
"Main contributions of the paper, one per entry."
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
layout.addWidget(self.contributions)
|
|
558
|
+
w.setLayout(layout)
|
|
559
|
+
return w
|
|
560
|
+
|
|
561
|
+
def _related_work_tab(self):
|
|
562
|
+
tabs = QTabWidget()
|
|
563
|
+
tabs.addTab(self._references_tab(), "References")
|
|
564
|
+
tabs.addTab(self._wrap_scroll(self._synthesis_tab()), "Human Curated Synthesis")
|
|
565
|
+
return tabs
|
|
566
|
+
|
|
567
|
+
def _references_tab(self):
|
|
568
|
+
w = QWidget()
|
|
569
|
+
main_layout = QVBoxLayout(w)
|
|
570
|
+
|
|
571
|
+
# ---- Lista de referências + painel de edição ----
|
|
572
|
+
content_layout = QHBoxLayout()
|
|
573
|
+
|
|
574
|
+
# Lista de referências
|
|
575
|
+
self.ref_list = QListWidget()
|
|
576
|
+
self.ref_list.setEditTriggers(
|
|
577
|
+
QListWidget.DoubleClicked | QListWidget.EditKeyPressed
|
|
578
|
+
)
|
|
579
|
+
self.ref_list.itemChanged.connect(self._on_reference_renamed)
|
|
580
|
+
self.ref_list.currentItemChanged.connect(self._load_reference)
|
|
581
|
+
content_layout.addWidget(self.ref_list, 1)
|
|
582
|
+
|
|
583
|
+
# Painel de edição (scrollable)
|
|
584
|
+
right_widget = QWidget()
|
|
585
|
+
right_layout = QVBoxLayout(right_widget)
|
|
586
|
+
|
|
587
|
+
self.ref_bibtex = LabeledTextEdit("BibTeX", "BibTeX entry. This is the only source for citations.")
|
|
588
|
+
self.ref_abstract = LabeledTextEdit("Abstract", "Original abstract of the cited paper.")
|
|
589
|
+
self.ref_category = LabeledLineEdit("Methodological Category", "e.g., deep_learning, transformer_based, graph_based.")
|
|
590
|
+
self.ref_contribution = LabeledTextEdit("Central Technical Idea", "Main technical idea introduced by this work.")
|
|
591
|
+
self.ref_strengths = StringListEditor("Author Reported Strengths", "Strengths explicitly claimed by the original authors.")
|
|
592
|
+
self.ref_limitations = StringListEditor("Reported Limitations", "Limitations discussed or implied by the paper.")
|
|
593
|
+
self.ref_relevance = LabeledTextEdit("Relevance to Our Work", "How this work relates to and differs from our paper.")
|
|
594
|
+
self.ref_role = LabeledLineEdit("Introduction Paragraph Role", "foundational, early_state_of_art, recent_advances, etc.")
|
|
595
|
+
|
|
596
|
+
for wdg in [
|
|
597
|
+
self.ref_bibtex, self.ref_abstract, self.ref_category,
|
|
598
|
+
self.ref_contribution, self.ref_strengths,
|
|
599
|
+
self.ref_limitations, self.ref_relevance, self.ref_role
|
|
600
|
+
]:
|
|
601
|
+
right_layout.addWidget(wdg)
|
|
602
|
+
|
|
603
|
+
scroll = QScrollArea()
|
|
604
|
+
scroll.setWidgetResizable(True)
|
|
605
|
+
scroll.setWidget(right_widget)
|
|
606
|
+
|
|
607
|
+
content_layout.addWidget(scroll, 3)
|
|
608
|
+
|
|
609
|
+
main_layout.addLayout(content_layout)
|
|
610
|
+
|
|
611
|
+
# ---- Botões fora do scroll ----
|
|
612
|
+
btns = QHBoxLayout()
|
|
613
|
+
|
|
614
|
+
add = QPushButton("Add Reference")
|
|
615
|
+
add.setIcon(QIcon.fromTheme("list-add"))
|
|
616
|
+
add.setIconSize(QSize(32, 32))
|
|
617
|
+
|
|
618
|
+
remove = QPushButton("Remove Reference")
|
|
619
|
+
remove.setIcon(QIcon.fromTheme("list-remove"))
|
|
620
|
+
remove.setIconSize(QSize(32, 32))
|
|
621
|
+
|
|
622
|
+
btns.addWidget(add)
|
|
623
|
+
btns.addWidget(remove)
|
|
624
|
+
|
|
625
|
+
add.clicked.connect(self._add_reference)
|
|
626
|
+
remove.clicked.connect(self._remove_reference)
|
|
627
|
+
|
|
628
|
+
main_layout.addLayout(btns)
|
|
629
|
+
|
|
630
|
+
w.setLayout(main_layout)
|
|
631
|
+
return w
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
def _on_reference_renamed(self, item: QListWidgetItem):
|
|
636
|
+
old_key = self.current_reference_key
|
|
637
|
+
new_key = item.text().strip()
|
|
638
|
+
|
|
639
|
+
if not old_key:
|
|
640
|
+
return
|
|
641
|
+
|
|
642
|
+
if not new_key:
|
|
643
|
+
QMessageBox.warning(self, "Invalid name", "Reference key cannot be empty.")
|
|
644
|
+
self.ref_list.blockSignals(True)
|
|
645
|
+
item.setText(old_key)
|
|
646
|
+
self.ref_list.blockSignals(False)
|
|
647
|
+
return
|
|
648
|
+
|
|
649
|
+
if new_key in self.references_data and new_key != old_key:
|
|
650
|
+
QMessageBox.warning(self, "Duplicate key", "This reference key already exists.")
|
|
651
|
+
self.ref_list.blockSignals(True)
|
|
652
|
+
item.setText(old_key)
|
|
653
|
+
self.ref_list.blockSignals(False)
|
|
654
|
+
return
|
|
655
|
+
|
|
656
|
+
self._save_current_reference()
|
|
657
|
+
|
|
658
|
+
self.references_data[new_key] = self.references_data.pop(old_key)
|
|
659
|
+
self.current_reference_key = new_key
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
def _synthesis_tab(self):
|
|
663
|
+
w = QWidget()
|
|
664
|
+
layout = QVBoxLayout()
|
|
665
|
+
|
|
666
|
+
self.syn_trends = StringListEditor(
|
|
667
|
+
"Common Trends",
|
|
668
|
+
"Observed trends across the literature."
|
|
669
|
+
)
|
|
670
|
+
self.syn_open = StringListEditor(
|
|
671
|
+
"Open Problems",
|
|
672
|
+
"Unresolved problems identified by the author."
|
|
673
|
+
)
|
|
674
|
+
self.syn_gap = LabeledTextEdit(
|
|
675
|
+
"Explicit Research Gap",
|
|
676
|
+
"Clear formulation of the research gap addressed by the paper."
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
for wdg in [self.syn_trends, self.syn_open, self.syn_gap]:
|
|
680
|
+
layout.addWidget(wdg)
|
|
681
|
+
|
|
682
|
+
w.setLayout(layout)
|
|
683
|
+
return w
|
|
684
|
+
|
|
685
|
+
def _writing_guidelines_tab(self):
|
|
686
|
+
w = QWidget()
|
|
687
|
+
layout = QVBoxLayout()
|
|
688
|
+
|
|
689
|
+
self.wg = LabeledTextEdit(
|
|
690
|
+
"Writing Guidelines",
|
|
691
|
+
"Explicit instructions to be followed by the LLM when generating text."
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
layout.addWidget(self.wg)
|
|
695
|
+
w.setLayout(layout)
|
|
696
|
+
return w
|
|
697
|
+
|
|
698
|
+
# ---------- Reference Helpers ----------
|
|
699
|
+
|
|
700
|
+
def _add_reference(self):
|
|
701
|
+
if self.references_data:
|
|
702
|
+
indices = []
|
|
703
|
+
for k in self.references_data.keys():
|
|
704
|
+
try:
|
|
705
|
+
indices.append(int(k.split("_")[1]))
|
|
706
|
+
except (IndexError, ValueError):
|
|
707
|
+
pass
|
|
708
|
+
next_idx = max(indices) + 1 if indices else 1
|
|
709
|
+
else:
|
|
710
|
+
next_idx = 1
|
|
711
|
+
|
|
712
|
+
key = f"ref_{next_idx}"
|
|
713
|
+
self.references_data[key] = {}
|
|
714
|
+
|
|
715
|
+
self.ref_list.blockSignals(True)
|
|
716
|
+
|
|
717
|
+
item = QListWidgetItem(key)
|
|
718
|
+
item.setFlags(item.flags() | Qt.ItemIsEditable)
|
|
719
|
+
self.ref_list.addItem(item)
|
|
720
|
+
self.ref_list.setCurrentItem(item)
|
|
721
|
+
|
|
722
|
+
self.current_reference_key = key
|
|
723
|
+
|
|
724
|
+
self.ref_list.blockSignals(False)
|
|
725
|
+
|
|
726
|
+
self._clear_reference_editor()
|
|
727
|
+
|
|
728
|
+
def _clear_reference_editor(self):
|
|
729
|
+
self.ref_bibtex.set("")
|
|
730
|
+
self.ref_abstract.set("")
|
|
731
|
+
self.ref_category.set("")
|
|
732
|
+
self.ref_contribution.set("")
|
|
733
|
+
self.ref_strengths.set([])
|
|
734
|
+
self.ref_limitations.set([])
|
|
735
|
+
self.ref_relevance.set("")
|
|
736
|
+
self.ref_role.set("")
|
|
737
|
+
|
|
738
|
+
def _remove_reference(self):
|
|
739
|
+
for item in self.ref_list.selectedItems():
|
|
740
|
+
key = item.text()
|
|
741
|
+
self.references_data.pop(key, None)
|
|
742
|
+
self.ref_list.takeItem(self.ref_list.row(item))
|
|
743
|
+
|
|
744
|
+
if key == self.current_reference_key:
|
|
745
|
+
self.current_reference_key = None
|
|
746
|
+
|
|
747
|
+
self._clear_reference_editor()
|
|
748
|
+
|
|
749
|
+
def _save_current_reference(self):
|
|
750
|
+
if not self.current_reference_key:
|
|
751
|
+
return
|
|
752
|
+
|
|
753
|
+
self.references_data[self.current_reference_key] = {
|
|
754
|
+
"bibtex": self.ref_bibtex.get(),
|
|
755
|
+
"abstract": self.ref_abstract.get(),
|
|
756
|
+
"methodological_category": self.ref_category.get(),
|
|
757
|
+
"central_technical_idea": self.ref_contribution.get(),
|
|
758
|
+
"author_reported_strengths": self.ref_strengths.get(),
|
|
759
|
+
"reported_limitations": self.ref_limitations.get(),
|
|
760
|
+
"relevance_to_our_work": self.ref_relevance.get(),
|
|
761
|
+
"introduction_paragraph_role": self.ref_role.get(),
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
def _load_reference(self, current, previous):
|
|
766
|
+
if previous and self.current_reference_key:
|
|
767
|
+
self._save_current_reference()
|
|
768
|
+
|
|
769
|
+
if not current:
|
|
770
|
+
self.current_reference_key = None
|
|
771
|
+
return
|
|
772
|
+
|
|
773
|
+
self.ref_list.blockSignals(True)
|
|
774
|
+
|
|
775
|
+
self.current_reference_key = current.text()
|
|
776
|
+
ref = self.references_data.get(self.current_reference_key, {})
|
|
777
|
+
|
|
778
|
+
self.ref_bibtex.set(ref.get("bibtex"))
|
|
779
|
+
self.ref_abstract.set(ref.get("abstract"))
|
|
780
|
+
self.ref_category.set(ref.get("methodological_category"))
|
|
781
|
+
self.ref_contribution.set(ref.get("central_technical_idea"))
|
|
782
|
+
self.ref_strengths.set(ref.get("author_reported_strengths", []))
|
|
783
|
+
self.ref_limitations.set(ref.get("reported_limitations", []))
|
|
784
|
+
self.ref_relevance.set(ref.get("relevance_to_our_work"))
|
|
785
|
+
self.ref_role.set(ref.get("introduction_paragraph_role"))
|
|
786
|
+
|
|
787
|
+
self.ref_list.blockSignals(False)
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
# ---------- Load / Save ----------
|
|
791
|
+
|
|
792
|
+
def load_json(self):
|
|
793
|
+
self._save_current_reference()
|
|
794
|
+
|
|
795
|
+
path, _ = QFileDialog.getOpenFileName(self, "Load JSON", "", "JSON Files (*.json)")
|
|
796
|
+
if not path:
|
|
797
|
+
return
|
|
798
|
+
|
|
799
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
800
|
+
data = json.load(f)
|
|
801
|
+
|
|
802
|
+
self.current_path = path
|
|
803
|
+
self.status.showMessage(f"Loaded from {path}")
|
|
804
|
+
|
|
805
|
+
# ---- Paper Profile ----
|
|
806
|
+
pp = data.get("paper_profile", {})
|
|
807
|
+
self.pp_title.set(pp.get("title"))
|
|
808
|
+
self.pp_domain.set(pp.get("domain"))
|
|
809
|
+
self.pp_journal.set(pp.get("target_journal"))
|
|
810
|
+
self.pp_keywords.set(pp.get("keywords", []))
|
|
811
|
+
self.pp_summary.set(pp.get("author_intended_summary"))
|
|
812
|
+
|
|
813
|
+
# ---- Research Problem ----
|
|
814
|
+
rp = data.get("research_problem", {})
|
|
815
|
+
self.rp_overview.set(rp.get("research_domain_overview"))
|
|
816
|
+
self.rp_specific.set(rp.get("specific_problem"))
|
|
817
|
+
self.rp_challenges.set(rp.get("practical_challenges", []))
|
|
818
|
+
self.rp_insufficient.set(rp.get("why_existing_solutions_are_insufficient"))
|
|
819
|
+
|
|
820
|
+
# ---- Contributions ----
|
|
821
|
+
self.contributions.set(data.get("contributions", []))
|
|
822
|
+
|
|
823
|
+
# ---- Writing Guidelines ----
|
|
824
|
+
self.wg.set(data.get("writing_guidelines", ""))
|
|
825
|
+
|
|
826
|
+
# ---- Related Work: References ----
|
|
827
|
+
self.references_data = data.get("related_work", {}).get("references", {})
|
|
828
|
+
self.ref_list.clear()
|
|
829
|
+
|
|
830
|
+
for key in self.references_data.keys():
|
|
831
|
+
item = QListWidgetItem(key)
|
|
832
|
+
item.setFlags(item.flags() | Qt.ItemIsEditable)
|
|
833
|
+
self.ref_list.addItem(item)
|
|
834
|
+
|
|
835
|
+
if self.ref_list.count() > 0:
|
|
836
|
+
self.ref_list.setCurrentRow(0)
|
|
837
|
+
|
|
838
|
+
# ---- Related Work: Human Curated Synthesis ----
|
|
839
|
+
synth = data.get("related_work", {}).get("human_curated_synthesis", {})
|
|
840
|
+
self.syn_trends.set(synth.get("common_trends", []))
|
|
841
|
+
self.syn_open.set(synth.get("open_problems", []))
|
|
842
|
+
self.syn_gap.set(synth.get("explicit_research_gap"))
|
|
843
|
+
|
|
844
|
+
def _obtaining_data(self):
|
|
845
|
+
self._save_current_reference()
|
|
846
|
+
|
|
847
|
+
data = {
|
|
848
|
+
"paper_profile": {
|
|
849
|
+
"title": self.pp_title.get(),
|
|
850
|
+
"domain": self.pp_domain.get(),
|
|
851
|
+
"target_journal": self.pp_journal.get(),
|
|
852
|
+
"keywords": self.pp_keywords.get(),
|
|
853
|
+
"author_intended_summary": self.pp_summary.get()
|
|
854
|
+
},
|
|
855
|
+
"research_problem": {
|
|
856
|
+
"research_domain_overview": self.rp_overview.get(),
|
|
857
|
+
"specific_problem": self.rp_specific.get(),
|
|
858
|
+
"practical_challenges": self.rp_challenges.get(),
|
|
859
|
+
"why_existing_solutions_are_insufficient": self.rp_insufficient.get()
|
|
860
|
+
},
|
|
861
|
+
"contributions": self.contributions.get(),
|
|
862
|
+
"related_work": {
|
|
863
|
+
"references": self.references_data,
|
|
864
|
+
"human_curated_synthesis": {
|
|
865
|
+
"common_trends": self.syn_trends.get(),
|
|
866
|
+
"open_problems": self.syn_open.get(),
|
|
867
|
+
"explicit_research_gap": self.syn_gap.get()
|
|
868
|
+
}
|
|
869
|
+
},
|
|
870
|
+
"writing_guidelines": self.wg.get()
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
return data
|
|
874
|
+
|
|
875
|
+
def save_as_json(self):
|
|
876
|
+
|
|
877
|
+
path, _ = QFileDialog.getSaveFileName(self, "Save JSON", "", "JSON Files (*.json)")
|
|
878
|
+
if not path:
|
|
879
|
+
return
|
|
880
|
+
|
|
881
|
+
data = self._obtaining_data()
|
|
882
|
+
|
|
883
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
884
|
+
json.dump(data, f, indent=2)
|
|
885
|
+
|
|
886
|
+
self.current_path = path
|
|
887
|
+
self.status.showMessage(f"Saved to {path}")
|
|
888
|
+
|
|
889
|
+
def is_data_empty(self, data: dict) -> bool:
|
|
890
|
+
def has_content(value):
|
|
891
|
+
if isinstance(value, str):
|
|
892
|
+
return bool(value.strip())
|
|
893
|
+
if isinstance(value, list):
|
|
894
|
+
return any(has_content(v) for v in value)
|
|
895
|
+
if isinstance(value, dict):
|
|
896
|
+
return any(has_content(v) for v in value.values())
|
|
897
|
+
return False
|
|
898
|
+
|
|
899
|
+
return not has_content(data)
|
|
900
|
+
|
|
901
|
+
def generate_intro(self):
|
|
902
|
+
global CONFIG_LLM
|
|
903
|
+
|
|
904
|
+
data = self._obtaining_data()
|
|
905
|
+
|
|
906
|
+
if self.is_data_empty(data):
|
|
907
|
+
QMessageBox.warning(
|
|
908
|
+
self,
|
|
909
|
+
"Missing data",
|
|
910
|
+
"Please fill at least one relevant field before generating the introduction."
|
|
911
|
+
)
|
|
912
|
+
return
|
|
913
|
+
|
|
914
|
+
if CONFIG_LLM["api_key"]=="":
|
|
915
|
+
CONFIG_LLM = configure.load_config(CONFIG_LLM_PATH)
|
|
916
|
+
|
|
917
|
+
if CONFIG_LLM["api_key"]=="":
|
|
918
|
+
self.status.showMessage("Open: " + CONFIG_LLM_PATH)
|
|
919
|
+
self._open_file_in_text_editor(CONFIG_LLM_PATH)
|
|
920
|
+
QDesktopServices.openUrl(QUrl(CONFIG_LLM["usage"]))
|
|
921
|
+
|
|
922
|
+
return
|
|
923
|
+
|
|
924
|
+
# Feedback visual
|
|
925
|
+
self.status.showMessage("Consulting LLM… please wait")
|
|
926
|
+
self.generate_intro_action.setEnabled(False)
|
|
927
|
+
|
|
928
|
+
# Thread
|
|
929
|
+
self.thread = QThread()
|
|
930
|
+
self.worker = ConsultationWorker(CONFIG_LLM, data)
|
|
931
|
+
|
|
932
|
+
self.worker.moveToThread(self.thread)
|
|
933
|
+
|
|
934
|
+
# Conexões
|
|
935
|
+
self.thread.started.connect(self.worker.run)
|
|
936
|
+
self.worker.finished.connect(self.on_intro_ready)
|
|
937
|
+
self.worker.error.connect(self.on_intro_error)
|
|
938
|
+
|
|
939
|
+
self.worker.finished.connect(self.thread.quit)
|
|
940
|
+
self.worker.finished.connect(self.worker.deleteLater)
|
|
941
|
+
self.thread.finished.connect(self.thread.deleteLater)
|
|
942
|
+
|
|
943
|
+
self.thread.start()
|
|
944
|
+
|
|
945
|
+
def on_intro_ready(self, out):
|
|
946
|
+
self.generate_intro_action.setEnabled(True)
|
|
947
|
+
self.status.showMessage("Done")
|
|
948
|
+
show_info_dialog(out, title_message="LLM response:")
|
|
949
|
+
|
|
950
|
+
def on_intro_error(self, error_msg):
|
|
951
|
+
self.generate_intro_action.setEnabled(True)
|
|
952
|
+
self.status.showMessage("Error")
|
|
953
|
+
show_error_dialog(error_msg)
|
|
954
|
+
|
|
955
|
+
# ---------- Main ----------
|
|
956
|
+
|
|
957
|
+
def main():
|
|
958
|
+
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
959
|
+
|
|
960
|
+
'''
|
|
961
|
+
create_desktop_directory()
|
|
962
|
+
create_desktop_menu()
|
|
963
|
+
create_desktop_file(os.path.join("~",".local","share","applications"),
|
|
964
|
+
program_name=about.__program_name__)
|
|
965
|
+
|
|
966
|
+
for n in range(len(sys.argv)):
|
|
967
|
+
if sys.argv[n] == "--autostart":
|
|
968
|
+
create_desktop_directory(overwrite = True)
|
|
969
|
+
create_desktop_menu(overwrite = True)
|
|
970
|
+
create_desktop_file(os.path.join("~",".config","autostart"),
|
|
971
|
+
overwrite=True,
|
|
972
|
+
program_name=about.__program_name__)
|
|
973
|
+
return
|
|
974
|
+
if sys.argv[n] == "--applications":
|
|
975
|
+
create_desktop_directory(overwrite = True)
|
|
976
|
+
create_desktop_menu(overwrite = True)
|
|
977
|
+
create_desktop_file(os.path.join("~",".local","share","applications"),
|
|
978
|
+
overwrite=True,
|
|
979
|
+
program_name=about.__program_name__)
|
|
980
|
+
return
|
|
981
|
+
'''
|
|
982
|
+
|
|
983
|
+
app = QApplication(sys.argv)
|
|
984
|
+
app.setApplicationName(about.__package__)
|
|
985
|
+
|
|
986
|
+
win = JsonIntroductionEditor()
|
|
987
|
+
win.show()
|
|
988
|
+
sys.exit(app.exec_())
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
if __name__ == "__main__":
|
|
992
|
+
main()
|
|
993
|
+
|