]> Softwares of Agnibho - medscript.git/blob - window.py
Check if background attr is in plugin
[medscript.git] / window.py
1 # MedScript
2 # Copyright (C) 2023 Dr. Agnibho Mondal
3 # This file is part of MedScript.
4 # MedScript is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
5 # MedScript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
6 # You should have received a copy of the GNU General Public License along with MedScript. If not, see <https://www.gnu.org/licenses/>.
7
8 import os, sys, datetime, dateutil.parser, shutil, json, threading
9 from PyQt6.QtCore import Qt, QDateTime, QSize, pyqtSignal
10 from PyQt6.QtWidgets import QWidget, QMainWindow, QMessageBox, QLabel, QPushButton, QLineEdit, QTextEdit, QDateTimeEdit, QListWidget, QComboBox, QCheckBox, QVBoxLayout, QHBoxLayout, QFormLayout, QToolBar, QTabWidget, QStatusBar, QFileDialog, QInputDialog, QCompleter, QSizePolicy
11 from PyQt6.QtGui import QAction, QIcon
12 from pathlib import Path
13 from hashlib import md5
14 from urllib import request
15 from packaging import version
16 from functools import partial
17
18 from config import config, info, real_dir
19 from prescription import Prescription
20 from renderer import Renderer
21 from filehandler import FileHandler
22 from renderbox import RenderBox
23 from setting import EditConfiguration, EditPrescriber
24 from editpreset import EditPreset
25 from viewbox import ViewBox
26 from preset import Preset
27 from tabular import Tabular
28 from index import Index
29 from plugin import Plugin
30
31 class MainWindow(QMainWindow):
32
33 signal_view=pyqtSignal(str)
34 signal_update=pyqtSignal(str)
35
36 current_file=FileHandler()
37 prescription=Prescription()
38 renderer=Renderer()
39 plugin=Plugin()
40 save_state=md5("".encode()).hexdigest()
41 unchanged_state=False
42
43 def cmd_new(self):
44 if(self.confirm_close()):
45 self.new_doc()
46
47 def cmd_open(self, file=None):
48 if(self.confirm_close()):
49 try:
50 self.current_file.reset()
51 if(file):
52 self.current_file.set_file(file)
53 else:
54 self.current_file.set_file(QFileDialog.getOpenFileName(self, "Open File", config["document_directory"], "Prescriptions (*.mpaz);; All Files (*)")[0])
55 self.current_file.open()
56 self.prescription.read_from(os.path.join(self.current_file.directory.name,"prescription.json"))
57 self.plugin.open(self.prescription)
58 self.load_interface_from_instance()
59
60 self.save_state=md5(self.prescription.get_json().encode()).hexdigest()
61 self.load_attachment(self.current_file.list())
62 self.unchanged_state=True
63 except Exception as e:
64 QMessageBox.warning(self,"Open failed", "Failed to open file.")
65 print(e)
66
67 def cmd_copy(self, data):
68 self.cmd_new()
69 self.prescription.name=data["name"]
70 self.prescription.age=data["age"]
71 self.prescription.sex=data["sex"]
72 self.prescription.address=data["address"]
73 self.prescription.contact=data["contact"]
74 self.load_interface_from_instance()
75
76 def cmd_save(self, save_as=False):
77 self.update_instance()
78 self.plugin.save(self.prescription)
79 if(self.input_template.currentText()!="<unchanged>"):
80 change_template=True
81 template=self.input_template.currentText()
82 else:
83 change_template=False
84 self.load_interface_from_instance()
85 suggest=self.prescription.id if(self.prescription.id) else self.prescription.name
86 suggest=os.path.abspath(os.path.join(config["document_directory"], suggest)+".mpaz")
87 if(save_as or not self.unchanged_state or QMessageBox.StandardButton.Yes==QMessageBox.question(self,"Confirm change", "Modify the original file?")):
88 try:
89 if not os.path.exists(self.current_file.file):
90 filename=QFileDialog.getSaveFileName(self, "Save File", suggest, "Prescriptions (*.mpaz);; All Files (*)")[0]
91 if(not filename.endswith(".mpaz")):
92 filename=filename+".mpaz"
93 self.current_file.set_file(filename)
94 for i in range(self.input_attachment.count()):
95 self.current_file.copy(self.input_attachment.item(i).text())
96 self.prescription.write_to(os.path.join(self.current_file.directory.name, "prescription.json"))
97 if change_template:
98 config["template"]=os.path.join(config["template_directory"], template)
99 self.current_file.save(change_template=change_template)
100 self.unchanged_state=False
101 self.load_interface_from_instance()
102 self.save_state=md5(self.prescription.get_json().encode()).hexdigest()
103 except Exception as e:
104 QMessageBox.warning(self,"Save failed", "Failed to save file.")
105 print(e)
106
107 def cmd_save_as(self):
108 suggest=self.prescription.id if(self.prescription.id) else self.prescription.name
109 suggest=os.path.abspath(os.path.join(config["document_directory"], suggest)+".mpaz")
110 self.current_file.set_file(QFileDialog.getSaveFileName(self, "Save File", suggest, "Prescriptions (*.mpaz);; All Files (*)")[0])
111 Path(self.current_file.file).touch()
112 self.cmd_save(save_as=True)
113
114 def cmd_refresh(self):
115 self.update_instance()
116 self.plugin.refresh(self.prescription)
117 self.load_interface_from_instance()
118 self.refresh()
119
120 def cmd_quit(self):
121 if(self.confirm_close()):
122 sys.exit()
123
124 def cmd_render(self):
125 self.refresh()
126 if(self.save_state==md5(self.prescription.get_json().encode()).hexdigest()):
127 try:
128 target=self.renderer.render(self.current_file.directory.name)
129 self.signal_view.emit(target)
130 self.renderbox.showMaximized()
131 except FileNotFoundError as e:
132 print(e)
133 QMessageBox.information(self, "Save first", "Please save the file before rendering.")
134
135 else:
136 QMessageBox.information(self, "Save first", "Please save the file before rendering.")
137
138 def cmd_sign(self):
139 self.refresh()
140 if(self.save_state==md5(self.prescription.get_json().encode()).hexdigest()):
141 ok=True #password, ok=QInputDialog.getText(self, "Enter password", "Private key password", QLineEdit.EchoMode.Password)
142 if(ok):
143 try:
144 try:
145 self.current_file.sign()
146 #self.current_file.sign(password)
147 self.cmd_save()
148 except FileNotFoundError as e:
149 print(e)
150 QMessageBox.information(self, "Save first", "Please save the file before signing.")
151 except TypeError as e:
152 print(e)
153 QMessageBox.information(self, "Configure", "Please add valid key and certificate to the config file.")
154 except EVPError as e:
155 print(e)
156 QMessageBox.information(self, "Check password", "Failed to load key. Please check if password is correct.")
157 except BIOError as e:
158 print(e)
159 QMessageBox.information(self, "Not found", "Certifcate and/or key not found.")
160 except SMIME_Error as e:
161 print(e)
162 QMessageBox.information(self, "Failed to load", "Failed to sign. Please check if certificate and key match.")
163 except Exception as e:
164 print(e)
165 QMessageBox.information(self, "Failed", "Failed to sign.")
166 except Exception as e:
167 print(e)
168 else:
169 QMessageBox.information(self, "Save first", "Please save the file before signing.")
170
171 def cmd_unsign(self):
172 self.current_file.delete_sign()
173 self.cmd_save()
174 self.refresh()
175
176 def cmd_verify(self):
177 try:
178 result=self.current_file.verify()
179 if result is False:
180 QMessageBox.critical(self, "Verification failed", "Signature is invalid.")
181 elif result is None:
182 QMessageBox.warning(self, "No Siganture", "No signature was found.")
183 else:
184 print(result)
185 QMessageBox.information(self, "Valid signature", "Valid signature found with the following information:\n"+result)
186 except FileNotFoundError as e:
187 print(e)
188 QMessageBox.warning(self, "No Siganture", "No signature was found.")
189 except Exception as e:
190 print(e)
191 QMessageBox.warning(self, "Failed", "Failed to verify.")
192
193 def cmd_tabular(self):
194 try:
195 filename=QFileDialog.getSaveFileName(self, "Export CSV File", os.path.join(config["data_directory"], "data.csv"), "CSV (*.csv);; All Files (*)")[0]
196 Tabular.export(filename)
197 QMessageBox.information(self, "Data Exported", "Data exported to."+filename)
198 except Exception as e:
199 print(e)
200 QMessageBox.critical(self, "Export failed", "Failed to export the data.")
201
202 def cmd_index(self):
203 self.index.refresh()
204 self.index.show()
205
206 def cmd_configuration(self):
207 self.edit_configuration.show()
208
209 def cmd_prescriber(self):
210 self.edit_prescriber.show()
211
212 def cmd_prescriber_reload(self, file=None):
213 self.prescription.reload_prescriber(file=None)
214 self.refresh()
215
216 def cmd_switch(self):
217 try:
218 self.prescription.reload_prescriber(QFileDialog.getOpenFileName(self, "Open File", config["prescriber_directory"], "JSON (*.json);; All Files (*)")[0])
219 self.refresh()
220 except FileNotFoundError as e:
221 print(e)
222
223 def cmd_preset(self):
224 self.edit_preset.show()
225
226 def cmd_about(self):
227 year=datetime.datetime.now().year
228 if(year>2023):
229 copy="2023"+"-"+str(year)
230 else:
231 copy="2023"
232 txt="<h1>MedScript</h1>"
233 txt=txt+"<p>Version "+info["version"]+"</p>"
234 txt=txt+"<p>The Prescription Writing Software</p>"
235 txt=txt+"<p><a href='"+info["url"]+"'>Website</a></p>"
236 txt=txt+"<p>Copyright © "+copy+" Dr. Agnibho Mondal</p>"
237 QMessageBox.about(self, "MedScript", txt)
238
239 def cmd_help(self):
240 self.viewbox.md(os.path.join(real_dir, "README"))
241 self.viewbox.show()
242
243 def cmd_update(self, silent=False):
244 try:
245 print("Current version "+info["version"])
246 with request.urlopen(info["url"]+"/info.json") as response:
247 latest=json.loads(response.read().decode())
248 print("Latest version "+latest["version"])
249 if(version.parse(info["version"]) < version.parse(latest["version"])):
250 self.signal_update.emit("New version <strong>"+latest["version"]+"</strong> available.<br>Visit <a href='"+latest["url"]+"'>"+latest["url"]+"</a> to get the latest version.")
251 elif(not silent):
252 self.signal_update.emit("No update available. You are using version "+info["version"]+".")
253 except Exception as e:
254 self.signal_update.emit("Failed to check available update.")
255 print(e)
256
257 def show_update(self, message):
258 QMessageBox.information(self, "Check update", message)
259
260 def insert_preset_extra(self):
261 try:
262 self.input_extra.insertPlainText(self.preset_extra.data[self.input_extra_preset.currentText()])
263 except KeyError:
264 self.input_extra.insertPlainText(self.input_extra_preset.currentText())
265 finally:
266 self.input_extra_preset.setCurrentIndex(-1)
267 if config["preset_newline"]:
268 self.input_extra.insertPlainText("\n")
269
270 def insert_preset_note(self):
271 try:
272 self.input_note.insertPlainText(self.preset_note.data[self.input_note_preset.currentText()])
273 except KeyError:
274 self.input_note.insertPlainText(self.input_note_preset.currentText())
275 finally:
276 self.input_note_preset.setCurrentIndex(-1)
277 if config["preset_newline"]:
278 self.input_note.insertPlainText("\n")
279
280 def insert_preset_report(self):
281 try:
282 self.input_report.insertPlainText(self.preset_report.data[self.input_report_preset.currentText()])
283 except KeyError:
284 self.input_report.insertPlainText(self.input_report_preset.currentText())
285 finally:
286 self.input_report_preset.setCurrentIndex(-1)
287 if config["preset_newline"]:
288 self.input_report.insertPlainText("\n")
289
290 def insert_preset_advice(self):
291 try:
292 self.input_advice.insertPlainText(self.preset_advice.data[self.input_advice_preset.currentText()])
293 except KeyError:
294 self.input_advice.insertPlainText(self.input_advice_preset.currentText())
295 finally:
296 self.input_advice_preset.setCurrentIndex(-1)
297 if config["preset_newline"]:
298 self.input_advice.insertPlainText("\n")
299
300 def insert_preset_investigation(self):
301 try:
302 self.input_investigation.insertPlainText(self.preset_investigation.data[self.input_investigation_preset.currentText()])
303 except KeyError:
304 self.input_investigation.insertPlainText(self.input_investigation_preset.currentText())
305 finally:
306 self.input_investigation_preset.setCurrentIndex(-1)
307 if config["preset_newline"]:
308 self.input_investigation.insertPlainText("\n")
309
310 def insert_preset_medication(self):
311 try:
312 self.input_medication.insertPlainText(self.preset_medication.data[self.input_medication_preset.currentText()])
313 except KeyError:
314 self.input_medication.insertPlainText(self.input_medication_preset.currentText())
315 finally:
316 self.input_medication_preset.setCurrentIndex(-1)
317 if config["preset_newline"]:
318 self.input_medication.insertPlainText("\n")
319
320 def insert_preset_additional(self):
321 try:
322 self.input_additional.insertPlainText(self.preset_additional.data[self.input_additional_preset.currentText()])
323 except KeyError:
324 self.input_additional.insertPlainText(self.input_additional_preset.currentText())
325 finally:
326 self.input_additional_preset.setCurrentIndex(-1)
327 if config["preset_newline"]:
328 self.input_additional.insertPlainText("\n")
329
330 def load_interface(self, file="", date=None, id="", name="", age="", sex="", address="", contact="", extra="", mode="", daw="", diagnosis="", note="", report="", advice="", investigation="", medication="", additional=""):
331 try:
332 file_msg=self.current_file.file if self.current_file.file else "New file"
333 sign_msg="(signed)" if config["smime"] and self.current_file.is_signed() else ""
334 self.statusbar.showMessage(file_msg+" "+sign_msg)
335 if date is None:
336 d=QDateTime.currentDateTime()
337 else:
338 try:
339 pdate=dateutil.parser.parse(date)
340 d=QDateTime.fromString(pdate.strftime("%Y-%m-%d %H:%M:%S"), "yyyy-MM-dd hh:mm:ss")
341 except Exception as e:
342 QMessageBox.warning(self,"Failed to load", str(e))
343 print(e)
344 self.input_date.setDateTime(d)
345 self.input_id.setText(id)
346 self.input_name.setText(name)
347 self.input_age.setText(age)
348 self.input_sex.setCurrentText(sex)
349 self.input_address.setText(address)
350 self.input_contact.setText(contact)
351 self.input_extra.setText(extra)
352 self.input_mode.setCurrentText(mode)
353 self.input_daw.setChecked(bool(daw))
354 self.input_diagnosis.setText(diagnosis)
355 self.input_note.setText(note)
356 self.input_report.setText(report)
357 self.input_advice.setText(advice)
358 self.input_investigation.setText(investigation)
359 self.input_medication.setText(medication)
360 self.input_additional.setText(additional)
361 self.label_prescriber.setText(self.prescription.prescriber.name)
362 except Exception as e:
363 QMessageBox.warning(self,"Failed to load", "Failed to load the data into the application.")
364 print(e)
365
366 def load_interface_from_instance(self):
367 if(self.current_file.has_template()):
368 if(self.input_template.findText("<unchanged>")==-1):
369 self.input_template.addItem("<unchanged>")
370 self.input_template.setCurrentText("<unchanged>")
371 else:
372 self.input_template.removeItem(self.input_template.findText("<unchanged>"))
373 self.load_interface(
374 file=self.prescription.file,
375 date=self.prescription.date,
376 id=self.prescription.id,
377 name=self.prescription.name,
378 age=self.prescription.age,
379 sex=self.prescription.sex,
380 address=self.prescription.address,
381 contact=self.prescription.contact,
382 extra=self.prescription.extra,
383 mode=self.prescription.mode,
384 daw=self.prescription.daw,
385 diagnosis=self.prescription.diagnosis,
386 note=self.prescription.note,
387 report=self.prescription.report,
388 advice=self.prescription.advice,
389 investigation=self.prescription.investigation,
390 medication=self.prescription.medication,
391 additional=self.prescription.additional
392 )
393
394 def update_instance(self):
395 try:
396 self.prescription.set_data(
397 date=self.input_date.dateTime().toString("yyyy-MM-dd hh:mm:ss"),
398 id=self.input_id.text(),
399 name=self.input_name.text(),
400 age=self.input_age.text(),
401 sex=self.input_sex.currentText(),
402 address=self.input_address.text(),
403 contact=self.input_contact.text(),
404 extra=self.input_extra.toPlainText(),
405 mode=self.input_mode.currentText(),
406 daw=self.input_daw.isChecked(),
407 diagnosis=self.input_diagnosis.text(),
408 note=self.input_note.toPlainText(),
409 report=self.input_report.toPlainText(),
410 advice=self.input_advice.toPlainText(),
411 investigation=self.input_investigation.toPlainText(),
412 medication=self.input_medication.toPlainText(),
413 additional=self.input_additional.toPlainText()
414 )
415 except Exception as e:
416 QMessageBox.critical(self,"Failed", "Critical failure happned. Please check console for more info.")
417 print(e)
418
419 def new_doc(self):
420 self.current_file.reset()
421 self.prescription.set_data()
422 self.input_attachment.clear()
423 self.load_interface()
424 self.update_instance()
425 self.plugin.new(self.prescription)
426 self.load_interface_from_instance()
427 self.save_state=md5(self.prescription.get_json().encode()).hexdigest()
428
429 def refresh(self):
430 self.update_instance()
431 self.load_interface_from_instance()
432
433 def add_attachment(self):
434 try:
435 new=QFileDialog.getOpenFileName(self, "Open File", config["document_directory"], "PDF (*.pdf);; Images (*.jpg, *.jpeg, *.png, *.gif);; All Files (*)")[0]
436 if new:
437 self.input_attachment.addItem(new)
438 except Exception as e:
439 QMessageBox.warning(self,"Attach failed", "Failed to attach file.")
440 print(e)
441
442 def remove_attachment(self):
443 index=self.input_attachment.currentRow()
444 if(index>=0):
445 self.current_file.delete_attachment(self.input_attachment.item(index).text())
446 self.input_attachment.takeItem(index)
447 else:
448 QMessageBox.warning(self, "Select item", "Please select an attachment to remove.")
449
450 def save_attachment(self):
451 try:
452 shutil.copyfile(self.input_attachment.currentItem().text(), QFileDialog.getSaveFileName(self, "Save Attachment", os.path.join(config["document_directory"], os.path.basename(self.input_attachment.currentItem().text())))[0])
453 except Exception as e:
454 print(e)
455
456 def load_attachment(self, attachments):
457 for attach in attachments:
458 self.input_attachment.addItem(attach)
459
460 def confirm_close(self):
461 self.refresh()
462 flag=(self.save_state==md5(self.prescription.get_json().encode()).hexdigest() or QMessageBox.StandardButton.Yes==QMessageBox.question(self,"Confirm action", "Unsaved changes may be lost. Continue?"))
463 return flag
464
465 def closeEvent(self, event):
466 if(self.confirm_close()):
467 event.accept()
468 else:
469 event.ignore()
470
471 def __init__(self, *args, **kwargs):
472 super().__init__(*args, **kwargs)
473
474 self.setWindowTitle("MedScript")
475 self.setGeometry(100, 100, 600, 400)
476 self.setWindowIcon(QIcon(os.path.join(config["resource"], "icon_medscript.ico")))
477
478 icon_open=QIcon(os.path.join(config["resource"], "icon_open.svg"))
479 icon_save=QIcon(os.path.join(config["resource"], "icon_save.svg"))
480 icon_render=QIcon(os.path.join(config["resource"], "icon_render.svg"))
481 icon_refresh=QIcon(os.path.join(config["resource"], "icon_refresh.svg"))
482
483 self.preset_extra=Preset(os.path.join(config["preset_directory"], "certify.csv"))
484 self.preset_note=Preset(os.path.join(config["preset_directory"], "note.csv"))
485 self.preset_report=Preset(os.path.join(config["preset_directory"], "report.csv"))
486 self.preset_advice=Preset(os.path.join(config["preset_directory"], "advice.csv"))
487 self.preset_investigation=Preset(os.path.join(config["preset_directory"], "investigation.csv"))
488 self.preset_medication=Preset(os.path.join(config["preset_directory"], "medication.csv"), text_as_key=True)
489 self.preset_additional=Preset(os.path.join(config["preset_directory"], "additional.csv"))
490
491 action_new=QAction("New", self)
492 action_new.setShortcut("Ctrl+N")
493 action_new.triggered.connect(self.cmd_new)
494 action_open=QAction("Open", self)
495 action_open2=QAction(icon_open, "Open", self)
496 action_open.setShortcut("Ctrl+O")
497 action_open.triggered.connect(self.cmd_open)
498 action_open2.triggered.connect(self.cmd_open)
499 action_save=QAction("Save", self)
500 action_save2=QAction(icon_save, "Save", self)
501 action_save.setShortcut("Ctrl+S")
502 action_save.triggered.connect(self.cmd_save)
503 action_save2.triggered.connect(self.cmd_save)
504 action_save_as=QAction("Save As", self)
505 action_save_as.setShortcut("Ctrl+Shift+S")
506 action_save_as.triggered.connect(self.cmd_save_as)
507 action_refresh=QAction("Refresh", self)
508 action_refresh.setShortcut("F5")
509 action_refresh2=QAction(icon_refresh, "Refresh", self)
510 action_refresh.triggered.connect(self.cmd_refresh)
511 action_refresh2.triggered.connect(self.cmd_refresh)
512 action_quit=QAction("Quit", self)
513 action_quit.setShortcut("Ctrl+Q")
514 action_quit.triggered.connect(self.cmd_quit)
515 action_render=QAction("Render", self)
516 action_render.setShortcut("Ctrl+R")
517 action_render2=QAction(icon_render, "Render", self)
518 action_render.triggered.connect(self.cmd_render)
519 action_render2.triggered.connect(self.cmd_render)
520 action_sign=QAction("Sign", self)
521 action_sign.triggered.connect(self.cmd_sign)
522 action_unsign=QAction("Unsign", self)
523 action_unsign.triggered.connect(self.cmd_unsign)
524 action_verify=QAction("Verify", self)
525 action_verify.triggered.connect(self.cmd_verify)
526 action_configuration=QAction("Configuration", self)
527 action_configuration.triggered.connect(self.cmd_configuration)
528 action_prescriber=QAction("Prescriber", self)
529 action_prescriber.triggered.connect(self.cmd_prescriber)
530 action_switch=QAction("Switch", self)
531 action_switch.triggered.connect(self.cmd_switch)
532 action_preset=QAction("Preset", self)
533 action_preset.triggered.connect(self.cmd_preset)
534 action_tabular=QAction("Tabular", self)
535 action_tabular.triggered.connect(self.cmd_tabular)
536 action_index=QAction("Index", self)
537 action_index.triggered.connect(self.cmd_index)
538 action_update=QAction("Update", self)
539 action_update.triggered.connect(self.cmd_update)
540 action_about=QAction("About", self)
541 action_about.triggered.connect(self.cmd_about)
542 action_help=QAction("Help", self)
543 action_help.setShortcut("F1")
544 action_help.triggered.connect(self.cmd_help)
545
546 menubar=self.menuBar()
547 menu_file=menubar.addMenu("File")
548 menu_file.addAction(action_new)
549 menu_file.addAction(action_open)
550 menu_file.addAction(action_save)
551 menu_file.addAction(action_save_as)
552 menu_file.addAction(action_quit)
553 menu_prepare=menubar.addMenu("Prepare")
554 menu_prepare.addAction(action_render)
555 menu_prepare.addAction(action_refresh)
556 if(config["smime"]):
557 menu_prepare.addAction(action_sign)
558 menu_prepare.addAction(action_unsign)
559 menu_prepare.addAction(action_verify)
560 menu_settings=menubar.addMenu("Settings")
561 menu_settings.addAction(action_configuration)
562 menu_settings.addAction(action_prescriber)
563 menu_settings.addAction(action_switch)
564 menu_settings.addAction(action_preset)
565 menu_data=menubar.addMenu("Data")
566 menu_data.addAction(action_index)
567 menu_data.addAction(action_tabular)
568
569 if(config["enable_plugin"]):
570 action_plugin=[]
571 try:
572 for i in self.plugin.commands():
573 action_plugin.append(QAction(i[1], self))
574 action_plugin[-1].triggered.connect(self.update_instance)
575 action_plugin[-1].triggered.connect(partial(self.plugin.run, i[0], self.prescription))
576 action_plugin[-1].triggered.connect(self.load_interface_from_instance)
577 except Exception as e:
578 print(e)
579 menu_plugin=menubar.addMenu("Plugin")
580 for i in action_plugin:
581 menu_plugin.addAction(i)
582
583 menu_help=menubar.addMenu("Help")
584 menu_help.addAction(action_update)
585 menu_help.addAction(action_about)
586 menu_help.addAction(action_help)
587
588 toolbar=QToolBar("Main Toolbar", floatable=False, movable=False)
589 toolbar.setIconSize(QSize(16, 16))
590 toolbar.addAction(action_open2)
591 toolbar.addAction(action_save2)
592 toolbar.addAction(action_refresh2)
593 toolbar.addAction(action_render2)
594 toolbar.addSeparator()
595 label_template=QLabel("Template:")
596 toolbar.addWidget(label_template)
597 self.input_template=QComboBox(self)
598 self.input_template.setMinimumWidth(200)
599 templates=os.listdir(config["template_directory"])
600 try:
601 templates.remove(os.path.basename(config["template"]))
602 templates.insert(0, os.path.basename(config["template"]))
603 except Exception as e:
604 print(e)
605 self.input_template.addItems(templates)
606 toolbar.addWidget(self.input_template)
607 spacer=QWidget(self)
608 spacer.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
609 toolbar.addWidget(spacer)
610 self.label_prescriber=QLabel(self)
611 toolbar.addWidget(self.label_prescriber)
612 self.addToolBar(toolbar)
613
614 tab_info=QWidget(self)
615 layout_info=QFormLayout(tab_info)
616 layout_info2=QHBoxLayout()
617 self.input_date=QDateTimeEdit(self)
618 self.input_date.setDisplayFormat("MMMM dd, yyyy hh:mm a")
619 layout_info.addRow("Date", self.input_date)
620 self.input_id=QLineEdit(self)
621 layout_info.addRow("ID", self.input_id)
622 self.input_name=QLineEdit(self)
623 layout_info.addRow("Name", self.input_name)
624 self.input_age=QLineEdit(self)
625 layout_info.addRow("Age", self.input_age)
626 self.input_sex=QComboBox(self)
627 self.input_sex.addItems(["Male", "Female", "Other"])
628 self.input_sex.setEditable(True)
629 layout_info.addRow("Sex", self.input_sex)
630 self.input_address=QLineEdit(self)
631 layout_info.addRow("Address", self.input_address)
632 self.input_contact=QLineEdit(self)
633 layout_info.addRow("Contact", self.input_contact)
634 self.input_diagnosis=QLineEdit(self)
635 layout_info.addRow("Diagnosis", self.input_diagnosis)
636 self.input_extra=QTextEdit(self)
637 self.input_extra_preset=QComboBox(self)
638 self.input_extra_preset.addItems(self.preset_extra.data.keys())
639 self.input_extra_preset.setCurrentIndex(-1)
640 self.input_extra_preset.setEditable(True)
641 self.input_extra_preset.completer().setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
642 self.input_extra_preset.completer().setFilterMode(Qt.MatchFlag.MatchContains)
643 self.input_extra_preset.setPlaceholderText("Select a preset")
644 input_extra_preset_btn=QPushButton("Insert")
645 input_extra_preset_btn.clicked.connect(self.insert_preset_extra)
646 layout_info2.addWidget(self.input_extra_preset, 5)
647 layout_info2.addWidget(input_extra_preset_btn, 1)
648 layout_info.addRow("Certify\nPreset", layout_info2)
649 layout_info.addRow("Certify /\nExtra", self.input_extra)
650 self.input_mode=QComboBox(self)
651 self.input_mode.addItems(["In-Person", "Tele-Consultation", "Other"])
652 self.input_mode.setEditable(True)
653 layout_info.addRow("Mode", self.input_mode)
654 self.input_daw=QCheckBox("Dispense as written", self)
655 layout_info.addRow("DAW", self.input_daw)
656
657 tab_note=QWidget(self)
658 layout_note=QVBoxLayout(tab_note)
659 layout_note2=QHBoxLayout()
660 label_note=QLabel("Clinical Notes")
661 label_note.setProperty("class", "info_head")
662 self.input_note_preset=QComboBox(self)
663 self.input_note_preset.addItems(self.preset_note.data.keys())
664 self.input_note_preset.setCurrentIndex(-1)
665 self.input_note_preset.setEditable(True)
666 self.input_note_preset.completer().setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
667 self.input_note_preset.completer().setFilterMode(Qt.MatchFlag.MatchContains)
668 self.input_note_preset.setPlaceholderText("Select a preset")
669 input_note_preset_btn=QPushButton("Insert")
670 input_note_preset_btn.clicked.connect(self.insert_preset_note)
671 layout_note2.addWidget(self.input_note_preset, 5)
672 layout_note2.addWidget(input_note_preset_btn, 1)
673 self.input_note=QTextEdit(self)
674 layout_note.addWidget(label_note)
675 layout_note.addLayout(layout_note2)
676 layout_note.addWidget(self.input_note)
677
678 tab_report=QWidget(self)
679 layout_report=QVBoxLayout(tab_report)
680 layout_report2=QHBoxLayout()
681 label_report=QLabel("Available Reports")
682 label_report.setProperty("class", "info_head")
683 self.input_report_preset=QComboBox(self)
684 self.input_report_preset.addItems(self.preset_report.data.keys())
685 self.input_report_preset.setCurrentIndex(-1)
686 self.input_report_preset.setEditable(True)
687 self.input_report_preset.completer().setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
688 self.input_report_preset.completer().setFilterMode(Qt.MatchFlag.MatchContains)
689 self.input_report_preset.setPlaceholderText("Select a preset")
690 input_report_preset_btn=QPushButton("Insert")
691 input_report_preset_btn.clicked.connect(self.insert_preset_report)
692 layout_report2.addWidget(self.input_report_preset, 5)
693 layout_report2.addWidget(input_report_preset_btn, 1)
694 self.input_report=QTextEdit(self)
695 layout_report.addWidget(label_report)
696 layout_report.addLayout(layout_report2)
697 layout_report.addWidget(self.input_report)
698
699 tab_advice=QWidget(self)
700 layout_advice=QVBoxLayout(tab_advice)
701 layout_advice2=QHBoxLayout()
702 label_advice=QLabel("Advice")
703 label_advice.setProperty("class", "info_head")
704 self.input_advice_preset=QComboBox(self)
705 self.input_advice_preset.addItems(self.preset_advice.data.keys())
706 self.input_advice_preset.setCurrentIndex(-1)
707 self.input_advice_preset.setEditable(True)
708 self.input_advice_preset.completer().setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
709 self.input_advice_preset.completer().setFilterMode(Qt.MatchFlag.MatchContains)
710 self.input_advice_preset.setPlaceholderText("Select a preset")
711 input_advice_preset_btn=QPushButton("Insert")
712 input_advice_preset_btn.clicked.connect(self.insert_preset_advice)
713 layout_advice2.addWidget(self.input_advice_preset, 5)
714 layout_advice2.addWidget(input_advice_preset_btn, 1)
715 self.input_advice=QTextEdit(self)
716 layout_advice.addWidget(label_advice)
717 layout_advice.addLayout(layout_advice2)
718 layout_advice.addWidget(self.input_advice)
719
720 tab_investigation=QWidget(self)
721 layout_investigation=QVBoxLayout(tab_investigation)
722 layout_investigation2=QHBoxLayout()
723 label_investigation=QLabel("Recommended Investigations")
724 label_investigation.setProperty("class", "info_head")
725 self.input_investigation_preset=QComboBox(self)
726 self.input_investigation_preset.addItems(self.preset_investigation.data.keys())
727 self.input_investigation_preset.setCurrentIndex(-1)
728 self.input_investigation_preset.setEditable(True)
729 self.input_investigation_preset.completer().setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
730 self.input_investigation_preset.completer().setFilterMode(Qt.MatchFlag.MatchContains)
731 self.input_investigation_preset.setPlaceholderText("Select a preset")
732 input_investigation_preset_btn=QPushButton("Insert")
733 input_investigation_preset_btn.clicked.connect(self.insert_preset_investigation)
734 layout_investigation2.addWidget(self.input_investigation_preset, 5)
735 layout_investigation2.addWidget(input_investigation_preset_btn, 1)
736 self.input_investigation=QTextEdit(self)
737 layout_investigation.addWidget(label_investigation)
738 layout_investigation.addLayout(layout_investigation2)
739 layout_investigation.addWidget(self.input_investigation)
740
741 tab_medication=QWidget(self)
742 layout_medication=QVBoxLayout(tab_medication)
743 layout_medication2=QHBoxLayout()
744 label_medication=QLabel("Medication Advice")
745 label_medication.setProperty("class", "info_head")
746 self.input_medication_preset=QComboBox(self)
747 self.input_medication_preset.addItems(self.preset_medication.data.keys())
748 self.input_medication_preset.setCurrentIndex(-1)
749 self.input_medication_preset.setEditable(True)
750 self.input_medication_preset.completer().setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
751 self.input_medication_preset.completer().setFilterMode(Qt.MatchFlag.MatchContains)
752 self.input_medication_preset.setPlaceholderText("Select a preset")
753 input_medication_preset_btn=QPushButton("Insert")
754 input_medication_preset_btn.clicked.connect(self.insert_preset_medication)
755 layout_medication2.addWidget(self.input_medication_preset, 5)
756 layout_medication2.addWidget(input_medication_preset_btn, 1)
757 self.input_medication=QTextEdit(self)
758 layout_medication.addWidget(label_medication)
759 layout_medication.addLayout(layout_medication2)
760 layout_medication.addWidget(self.input_medication)
761
762 tab_additional=QWidget(self)
763 layout_additional=QVBoxLayout(tab_additional)
764 layout_additional2=QHBoxLayout()
765 label_additional=QLabel("Additional Advice")
766 label_additional.setProperty("class", "info_head")
767 self.input_additional_preset=QComboBox(self)
768 self.input_additional_preset.addItems(self.preset_additional.data.keys())
769 self.input_additional_preset.setCurrentIndex(-1)
770 self.input_additional_preset.setEditable(True)
771 self.input_additional_preset.completer().setCompletionMode(QCompleter.CompletionMode.PopupCompletion)
772 self.input_additional_preset.completer().setFilterMode(Qt.MatchFlag.MatchContains)
773 self.input_additional_preset.setPlaceholderText("Select a preset")
774 input_additional_preset_btn=QPushButton("Insert")
775 input_additional_preset_btn.clicked.connect(self.insert_preset_additional)
776 layout_additional2.addWidget(self.input_additional_preset, 5)
777 layout_additional2.addWidget(input_additional_preset_btn, 1)
778 self.input_additional=QTextEdit(self)
779 layout_additional.addWidget(label_additional)
780 layout_additional.addLayout(layout_additional2)
781 layout_additional.addWidget(self.input_additional)
782
783 tab_attachment=QWidget(self)
784 layout_attachment=QVBoxLayout(tab_attachment)
785 layout_attachment2=QHBoxLayout()
786 label_attachment=QLabel("Attached files")
787 label_attachment.setProperty("class", "info_head")
788 self.input_attachment=QListWidget(self)
789 button_add=QPushButton("Add")
790 button_add.clicked.connect(self.add_attachment)
791 button_remove=QPushButton("Remove")
792 button_remove.clicked.connect(self.remove_attachment)
793 button_save=QPushButton("Save")
794 button_save.clicked.connect(self.save_attachment)
795 layout_attachment.addWidget(label_attachment)
796 layout_attachment.addLayout(layout_attachment2)
797 layout_attachment.addWidget(self.input_attachment)
798 layout_attachment2.addWidget(button_add)
799 layout_attachment2.addWidget(button_remove)
800 layout_attachment2.addWidget(button_save)
801
802 tab=QTabWidget(self)
803 tab.addTab(tab_info, "Patient")
804 tab.addTab(tab_note, "Clinical")
805 tab.addTab(tab_report, "Report")
806 tab.addTab(tab_advice, "Advice")
807 tab.addTab(tab_investigation, "Investigation")
808 tab.addTab(tab_medication, "Medication")
809 tab.addTab(tab_additional, "Additional")
810 tab.addTab(tab_attachment, "Attachment")
811
812 self.setCentralWidget(tab)
813
814 self.statusbar=QStatusBar()
815 self.setStatusBar(self.statusbar)
816
817 self.renderbox=RenderBox()
818 self.signal_view.connect(self.renderbox.update)
819 self.edit_configuration=EditConfiguration()
820 self.edit_prescriber=EditPrescriber()
821 self.edit_prescriber.signal_save.connect(self.cmd_prescriber_reload)
822 self.viewbox=ViewBox()
823 self.index=Index()
824 self.edit_preset=EditPreset()
825 self.index.signal_open.connect(self.cmd_open)
826 self.index.signal_copy.connect(self.cmd_copy)
827 self.signal_update.connect(self.show_update)
828
829 self.new_doc()
830 if(config["filename"]):
831 self.cmd_open(config["filename"])
832
833 if(len(self.prescription.prescriber.name.strip())<1):
834 self.cmd_prescriber()
835
836 if(config["check_update"]):
837 threading.Thread(target=self.cmd_update, args=[True]).start()
838
839 self.setWindowIcon(QIcon(os.path.join(config["resource"], "icon_medscript.ico")))
840 self.showMaximized()