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/>.
8 import os
, sys
, datetime
, dateutil
.parser
, shutil
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
15 from config
import config
, sign_available
16 from prescription
import Prescription
17 from renderer
import Renderer
18 from filehandler
import FileHandler
19 from renderbox
import RenderBox
20 from setting
import EditConfiguration
, EditPrescriber
21 from viewbox
import ViewBox
22 from preset
import Preset
24 from M2Crypto
.EVP
import EVPError
25 from M2Crypto
.BIO
import BIOError
26 from M2Crypto
.SMIME
import SMIME_Error
27 except Exception as e
:
31 class MainWindow(QMainWindow
):
33 signal_view
=pyqtSignal(str)
35 current_file
=FileHandler()
36 prescription
=Prescription()
38 save_state
=md5("".encode()).hexdigest()
42 if(self
.confirm_close()):
45 def cmd_open(self
, file=None):
46 if(self
.confirm_close()):
48 self
.current_file
.reset()
50 self
.current_file
.set_file(file)
52 self
.current_file
.set_file(QFileDialog
.getOpenFileName(self
, "Open File", config
["document_directory"], "Prescriptions (*.mpaz);; All Files (*)")[0])
53 self
.current_file
.open()
54 self
.prescription
.read_from(os
.path
.join(self
.current_file
.directory
.name
,"prescription.json"))
55 self
.load_interface_from_instance()
56 self
.save_state
=md5(self
.prescription
.get_json().encode()).hexdigest()
57 self
.load_attachment(self
.current_file
.list())
58 self
.unchanged_state
=True
59 except Exception as e
:
60 QMessageBox
.warning(self
,"Open failed", "Failed to open file.")
63 def cmd_save(self
, save_as
=False):
64 self
.update_instance()
65 suggest
=self
.prescription
.id if(self
.prescription
.id) else self
.prescription
.name
66 suggest
=os
.path
.abspath(os
.path
.join(config
["document_directory"], suggest
)+".mpaz")
67 if(save_as
or not self
.unchanged_state
or QMessageBox
.StandardButton
.Yes
==QMessageBox
.question(self
,"Confirm change", "Modify the original file?")):
69 if not os
.path
.exists(self
.current_file
.file):
70 filename
=QFileDialog
.getSaveFileName(self
, "Save File", suggest
, "Prescriptions (*.mpaz);; All Files (*)")[0]
71 self
.current_file
.set_file(filename
)
72 for i
in range(self
.input_attachment
.count()):
73 self
.current_file
.copy(self
.input_attachment
.item(i
).text())
74 self
.prescription
.write_to(os
.path
.join(self
.current_file
.directory
.name
, "prescription.json"))
75 config
["template"]=os
.path
.join(config
["template_directory"], self
.input_template
.currentText())
76 self
.current_file
.save()
77 self
.unchanged_state
=False
78 self
.load_interface_from_instance()
79 self
.save_state
=md5(self
.prescription
.get_json().encode()).hexdigest()
80 except Exception as e
:
81 QMessageBox
.warning(self
,"Save failed", "Failed to save file.")
84 def cmd_save_as(self
):
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 self
.current_file
.set_file(QFileDialog
.getSaveFileName(self
, "Save File", suggest
, "Prescriptions (*.mpaz);; All Files (*)")[0])
88 Path(self
.current_file
.file).touch()
89 self
.cmd_save(save_as
=True)
91 def cmd_refresh(self
):
95 if(self
.confirm_close()):
100 if(self
.save_state
==md5(self
.prescription
.get_json().encode()).hexdigest()):
102 target
=self
.renderer
.render(self
.current_file
.directory
.name
)
103 self
.signal_view
.emit(target
)
104 self
.renderbox
.showMaximized()
105 except FileNotFoundError
as e
:
107 QMessageBox
.information(self
, "Save first", "Please save the file before rendering.")
110 QMessageBox
.information(self
, "Save first", "Please save the file before rendering.")
114 if(self
.save_state
==md5(self
.prescription
.get_json().encode()).hexdigest()):
115 password
, ok
=QInputDialog
.getText(self
, "Enter password", "Private key password", QLineEdit
.EchoMode
.Password
)
119 self
.current_file
.sign(password
)
121 except FileNotFoundError
as e
:
123 QMessageBox
.information(self
, "Save first", "Please save the file before signing.")
124 except TypeError as e
:
126 QMessageBox
.information(self
, "Configure", "Please add valid key and certificate to the config file.")
127 except EVPError
as e
:
129 QMessageBox
.information(self
, "Check password", "Failed to load key. Please check if password is correct.")
130 except BIOError
as e
:
132 QMessageBox
.information(self
, "Not found", "Certifcate and/or key not found.")
133 except SMIME_Error
as e
:
135 QMessageBox
.information(self
, "Failed to load", "Failed to sign. Please check if certificate and key match.")
136 except Exception as e
:
138 QMessageBox
.information(self
, "Failed", "Failed to sign.")
139 except Exception as e
:
142 QMessageBox
.information(self
, "Save first", "Please save the file before signing.")
144 def cmd_unsign(self
):
145 self
.current_file
.delete_sign()
149 def cmd_verify(self
):
151 result
=self
.current_file
.verify()
153 QMessageBox
.critical(self
, "Verification failed", "Signature is invalid.")
155 QMessageBox
.warning(self
, "No Siganture", "No signature was found.")
158 QMessageBox
.information(self
, "Valid signature", "Valid signature found with the following information:\n"+result
)
159 except FileNotFoundError
as e
:
161 QMessageBox
.warning(self
, "No Siganture", "No signature was found.")
162 except Exception as e
:
164 QMessageBox
.warning(self
, "Failed", "Failed to verify.")
166 def cmd_configuration(self
):
167 self
.edit_configuration
.show()
169 def cmd_prescriber(self
):
170 self
.edit_prescriber
.show()
172 def cmd_prescriber_reload(self
, file=None):
173 self
.prescription
.reload_prescriber(file=None)
176 def cmd_switch(self
):
178 self
.prescription
.reload_prescriber(QFileDialog
.getOpenFileName(self
, "Open File", config
["prescriber_directory"], "JSON (*.json);; All Files (*)")[0])
180 except FileNotFoundError
as e
:
184 self
.viewbox
.open(os
.path
.join(config
["resource"], "about.html"))
188 self
.viewbox
.open(os
.path
.join(config
["resource"], "help.html"))
191 def insert_preset_extra(self
):
193 self
.input_extra
.insertPlainText(self
.preset_extra
.data
[self
.input_extra_preset
.currentText()])
195 self
.input_extra
.insertPlainText(self
.input_extra_preset
.currentText())
197 self
.input_extra_preset
.setCurrentIndex(-1)
198 if config
["preset_newline"]:
199 self
.input_extra
.insertPlainText("\n")
201 def insert_preset_note(self
):
203 self
.input_note
.insertPlainText(self
.preset_note
.data
[self
.input_note_preset
.currentText()])
205 self
.input_note
.insertPlainText(self
.input_note_preset
.currentText())
207 self
.input_note_preset
.setCurrentIndex(-1)
208 if config
["preset_newline"]:
209 self
.input_note
.insertPlainText("\n")
211 def insert_preset_report(self
):
213 self
.input_report
.insertPlainText(self
.preset_report
.data
[self
.input_report_preset
.currentText()])
215 self
.input_report
.insertPlainText(self
.input_report_preset
.currentText())
217 self
.input_report_preset
.setCurrentIndex(-1)
218 if config
["preset_newline"]:
219 self
.input_report
.insertPlainText("\n")
220 def insert_preset_advice(self
):
222 self
.input_advice
.insertPlainText(self
.preset_advice
.data
[self
.input_advice_preset
.currentText()])
224 self
.input_advice
.insertPlainText(self
.input_advice_preset
.currentText())
226 self
.input_advice_preset
.setCurrentIndex(-1)
227 if config
["preset_newline"]:
228 self
.input_advice
.insertPlainText("\n")
231 def insert_preset_investigation(self
):
233 self
.input_investigation
.insertPlainText(self
.preset_investigation
.data
[self
.input_investigation_preset
.currentText()])
235 self
.input_investigation
.insertPlainText(self
.input_investigation_preset
.currentText())
237 self
.input_investigation_preset
.setCurrentIndex(-1)
238 if config
["preset_newline"]:
239 self
.input_investigation
.insertPlainText("\n")
241 def insert_preset_medication(self
):
243 self
.input_medication
.insertPlainText(self
.preset_medication
.data
[self
.input_medication_preset
.currentText()])
245 self
.input_medication
.insertPlainText(self
.input_medication_preset
.currentText())
247 self
.input_medication_preset
.setCurrentIndex(-1)
248 if config
["preset_newline"]:
249 self
.input_medication
.insertPlainText("\n")
251 def insert_preset_additional(self
):
253 self
.input_additional
.insertPlainText(self
.preset_additional
.data
[self
.input_additional_preset
.currentText()])
255 self
.input_additional
.insertPlainText(self
.input_additional_preset
.currentText())
257 self
.input_additional_preset
.setCurrentIndex(-1)
258 if config
["preset_newline"]:
259 self
.input_additional
.insertPlainText("\n")
261 def load_interface(self
, file="", date
=None, id="", name
="", age
="", sex
="", address
="", contact
="", extra
="", mode
="", daw
="", diagnosis
="", note
="", report
="", advice
="", investigation
="", medication
="", additional
=""):
263 file_msg
=self
.current_file
.file if self
.current_file
.file else "New file"
264 sign_msg
="(signed)" if sign_available
and config
["smime"] and self
.current_file
.is_signed() else ""
265 self
.statusbar
.showMessage(file_msg
+" "+sign_msg
)
267 d
=QDateTime
.currentDateTime()
270 pdate
=dateutil
.parser
.parse(date
)
271 d
=QDateTime
.fromString(pdate
.strftime("%Y-%m-%d %H:%M:%S"), "yyyy-MM-dd hh:mm:ss")
272 except Exception as e
:
273 QMessageBox
.warning(self
,"Failed to load", str(e
))
275 self
.input_date
.setDateTime(d
)
276 self
.input_id
.setText(id)
277 self
.input_name
.setText(name
)
278 self
.input_age
.setText(age
)
279 self
.input_sex
.setCurrentText(sex
)
280 self
.input_address
.setText(address
)
281 self
.input_contact
.setText(contact
)
282 self
.input_extra
.setText(extra
)
283 self
.input_mode
.setCurrentText(mode
)
284 self
.input_daw
.setChecked(bool(daw
))
285 self
.input_diagnosis
.setText(diagnosis
)
286 self
.input_note
.setText(note
)
287 self
.input_report
.setText(report
)
288 self
.input_advice
.setText(advice
)
289 self
.input_investigation
.setText(investigation
)
290 self
.input_medication
.setText(medication
)
291 self
.input_additional
.setText(additional
)
292 self
.label_prescriber
.setText(self
.prescription
.prescriber
.name
)
293 except Exception as e
:
294 QMessageBox
.warning(self
,"Failed to load", "Failed to load the data into the application.")
297 def load_interface_from_instance(self
):
299 file=self
.prescription
.file,
300 date
=self
.prescription
.date
,
301 id=self
.prescription
.id,
302 name
=self
.prescription
.name
,
303 age
=self
.prescription
.age
,
304 sex
=self
.prescription
.sex
,
305 address
=self
.prescription
.address
,
306 contact
=self
.prescription
.contact
,
307 extra
=self
.prescription
.extra
,
308 mode
=self
.prescription
.mode
,
309 daw
=self
.prescription
.daw
,
310 diagnosis
=self
.prescription
.diagnosis
,
311 note
=self
.prescription
.note
,
312 report
=self
.prescription
.report
,
313 advice
=self
.prescription
.advice
,
314 investigation
=self
.prescription
.investigation
,
315 medication
=self
.prescription
.medication
,
316 additional
=self
.prescription
.additional
319 def update_instance(self
):
321 self
.prescription
.set_data(
322 date
=self
.input_date
.dateTime().toString("yyyy-MM-dd hh:mm:ss"),
323 id=self
.input_id
.text(),
324 name
=self
.input_name
.text(),
325 age
=self
.input_age
.text(),
326 sex
=self
.input_sex
.currentText(),
327 address
=self
.input_address
.text(),
328 contact
=self
.input_contact
.text(),
329 extra
=self
.input_extra
.toPlainText(),
330 mode
=self
.input_mode
.currentText(),
331 daw
=self
.input_daw
.isChecked(),
332 diagnosis
=self
.input_diagnosis
.text(),
333 note
=self
.input_note
.toPlainText(),
334 report
=self
.input_report
.toPlainText(),
335 advice
=self
.input_advice
.toPlainText(),
336 investigation
=self
.input_investigation
.toPlainText(),
337 medication
=self
.input_medication
.toPlainText(),
338 additional
=self
.input_additional
.toPlainText()
340 except Exception as e
:
341 QMessageBox
.critical(self
,"Failed", "Critical failure happned. Please check console for more info.")
345 self
.current_file
.reset()
346 self
.prescription
.set_data()
347 self
.input_attachment
.clear()
348 self
.load_interface()
349 self
.update_instance()
350 self
.save_state
=md5(self
.prescription
.get_json().encode()).hexdigest()
353 self
.update_instance()
354 self
.load_interface_from_instance()
356 def add_attachment(self
):
358 new
=QFileDialog
.getOpenFileName(self
, "Open File", config
["document_directory"], "PDF (*.pdf);; Images (*.jpg, *.jpeg, *.png, *.gif);; All Files (*)")[0]
360 self
.input_attachment
.addItem(new
)
361 except Exception as e
:
362 QMessageBox
.warning(self
,"Attach failed", "Failed to attach file.")
365 def remove_attachment(self
):
366 index
=self
.input_attachment
.currentRow()
368 self
.current_file
.delete_attachment(self
.input_attachment
.item(index
).text())
369 self
.input_attachment
.takeItem(index
)
371 QMessageBox
.warning(self
, "Select item", "Please select an attachment to remove.")
373 def save_attachment(self
):
375 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])
376 except Exception as e
:
379 def load_attachment(self
, attachments
):
380 for attach
in attachments
:
381 self
.input_attachment
.addItem(attach
)
383 def confirm_close(self
):
385 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?"))
388 def closeEvent(self
, event
):
389 if(self
.confirm_close()):
394 def __init__(self
, *args
, **kwargs
):
395 super().__init
__(*args
, **kwargs
)
397 global sign_available
399 self
.setWindowTitle("MedScript")
400 self
.setGeometry(100, 100, 600, 400)
401 self
.setWindowIcon(QIcon(os
.path
.join(config
["resource"], "icon_medscript.ico")))
403 icon_open
=QIcon(os
.path
.join(config
["resource"], "icon_open.svg"))
404 icon_save
=QIcon(os
.path
.join(config
["resource"], "icon_save.svg"))
405 icon_render
=QIcon(os
.path
.join(config
["resource"], "icon_render.svg"))
406 icon_refresh
=QIcon(os
.path
.join(config
["resource"], "icon_refresh.svg"))
408 self
.preset_extra
=Preset(os
.path
.join(config
["preset_directory"], "certify.csv"))
409 self
.preset_note
=Preset(os
.path
.join(config
["preset_directory"], "note.csv"))
410 self
.preset_report
=Preset(os
.path
.join(config
["preset_directory"], "report.csv"))
411 self
.preset_advice
=Preset(os
.path
.join(config
["preset_directory"], "advice.csv"))
412 self
.preset_investigation
=Preset(os
.path
.join(config
["preset_directory"], "investigation.csv"))
413 self
.preset_medication
=Preset(os
.path
.join(config
["preset_directory"], "medication.csv"), text_as_key
=True)
414 self
.preset_additional
=Preset(os
.path
.join(config
["preset_directory"], "additional.csv"))
416 action_new
=QAction("New", self
)
417 action_new
.setShortcut("Ctrl+N")
418 action_new
.triggered
.connect(self
.cmd_new
)
419 action_open
=QAction("Open", self
)
420 action_open2
=QAction(icon_open
, "Open", self
)
421 action_open
.setShortcut("Ctrl+O")
422 action_open
.triggered
.connect(self
.cmd_open
)
423 action_open2
.triggered
.connect(self
.cmd_open
)
424 action_save
=QAction("Save", self
)
425 action_save2
=QAction(icon_save
, "Save", self
)
426 action_save
.setShortcut("Ctrl+S")
427 action_save
.triggered
.connect(self
.cmd_save
)
428 action_save2
.triggered
.connect(self
.cmd_save
)
429 action_save_as
=QAction("Save As", self
)
430 action_save_as
.setShortcut("Ctrl+Shift+S")
431 action_save_as
.triggered
.connect(self
.cmd_save_as
)
432 action_refresh
=QAction("Refresh", self
)
433 action_refresh
.setShortcut("F5")
434 action_refresh2
=QAction(icon_refresh
, "Refresh", self
)
435 action_refresh
.triggered
.connect(self
.cmd_refresh
)
436 action_refresh2
.triggered
.connect(self
.cmd_refresh
)
437 action_quit
=QAction("Quit", self
)
438 action_quit
.setShortcut("Ctrl+Q")
439 action_quit
.triggered
.connect(self
.cmd_quit
)
440 action_render
=QAction("Render", self
)
441 action_render
.setShortcut("Ctrl+R")
442 action_render2
=QAction(icon_render
, "Render", self
)
443 action_render
.triggered
.connect(self
.cmd_render
)
444 action_render2
.triggered
.connect(self
.cmd_render
)
445 action_sign
=QAction("Sign", self
)
446 action_sign
.triggered
.connect(self
.cmd_sign
)
447 action_unsign
=QAction("Unsign", self
)
448 action_unsign
.triggered
.connect(self
.cmd_unsign
)
449 action_verify
=QAction("Verify", self
)
450 action_verify
.triggered
.connect(self
.cmd_verify
)
451 action_configuration
=QAction("Configuration", self
)
452 action_configuration
.triggered
.connect(self
.cmd_configuration
)
453 action_prescriber
=QAction("Prescriber", self
)
454 action_prescriber
.triggered
.connect(self
.cmd_prescriber
)
455 action_switch
=QAction("Switch", self
)
456 action_switch
.triggered
.connect(self
.cmd_switch
)
457 action_about
=QAction("About", self
)
458 action_about
.triggered
.connect(self
.cmd_about
)
459 action_help
=QAction("Help", self
)
460 action_help
.setShortcut("F1")
461 action_help
.triggered
.connect(self
.cmd_help
)
463 menubar
=self
.menuBar()
464 menu_file
=menubar
.addMenu("File")
465 menu_file
.addAction(action_new
)
466 menu_file
.addAction(action_open
)
467 menu_file
.addAction(action_save
)
468 menu_file
.addAction(action_save_as
)
469 menu_file
.addAction(action_quit
)
470 menu_prepare
=menubar
.addMenu("Prepare")
471 menu_prepare
.addAction(action_render
)
472 menu_prepare
.addAction(action_refresh
)
473 if(sign_available
and config
["smime"]):
474 menu_prepare
.addAction(action_sign
)
475 menu_prepare
.addAction(action_unsign
)
476 menu_prepare
.addAction(action_verify
)
477 menu_settings
=menubar
.addMenu("Settings")
478 menu_settings
.addAction(action_configuration
)
479 menu_settings
.addAction(action_prescriber
)
480 menu_settings
.addAction(action_switch
)
481 menu_help
=menubar
.addMenu("Help")
482 menu_help
.addAction(action_about
)
483 menu_help
.addAction(action_help
)
485 toolbar
=QToolBar("Main Toolbar", floatable
=False, movable
=False)
486 toolbar
.setIconSize(QSize(16, 16))
487 toolbar
.addAction(action_open2
)
488 toolbar
.addAction(action_save2
)
489 toolbar
.addAction(action_refresh2
)
490 toolbar
.addAction(action_render2
)
491 toolbar
.addSeparator()
492 self
.input_template
=QComboBox(self
)
493 templates
=os
.listdir(config
["template_directory"])
495 templates
.remove(os
.path
.basename(config
["template"]))
496 templates
.insert(0, os
.path
.basename(config
["template"]))
497 except Exception as e
:
499 self
.input_template
.addItems(templates
)
500 toolbar
.addWidget(self
.input_template
)
502 spacer
.setSizePolicy(QSizePolicy
.Policy
.Expanding
, QSizePolicy
.Policy
.Expanding
)
503 toolbar
.addWidget(spacer
)
504 self
.label_prescriber
=QLabel(self
)
505 toolbar
.addWidget(self
.label_prescriber
)
506 self
.addToolBar(toolbar
)
508 tab_info
=QWidget(self
)
509 layout_info
=QFormLayout(tab_info
)
510 layout_info2
=QHBoxLayout()
511 self
.input_date
=QDateTimeEdit(self
)
512 self
.input_date
.setDisplayFormat("MMMM dd, yyyy hh:mm a")
513 layout_info
.addRow("Date", self
.input_date
)
514 self
.input_id
=QLineEdit(self
)
515 layout_info
.addRow("ID", self
.input_id
)
516 self
.input_name
=QLineEdit(self
)
517 layout_info
.addRow("Name", self
.input_name
)
518 self
.input_age
=QLineEdit(self
)
519 layout_info
.addRow("Age", self
.input_age
)
520 self
.input_sex
=QComboBox(self
)
521 self
.input_sex
.addItems(["Male", "Female", "Other"])
522 self
.input_sex
.setEditable(True)
523 layout_info
.addRow("Sex", self
.input_sex
)
524 self
.input_address
=QLineEdit(self
)
525 layout_info
.addRow("Address", self
.input_address
)
526 self
.input_contact
=QLineEdit(self
)
527 layout_info
.addRow("Contact", self
.input_contact
)
528 self
.input_diagnosis
=QLineEdit(self
)
529 layout_info
.addRow("Diagnosis", self
.input_diagnosis
)
530 self
.input_extra
=QTextEdit(self
)
531 self
.input_extra_preset
=QComboBox(self
)
532 self
.input_extra_preset
.addItems(self
.preset_extra
.data
.keys())
533 self
.input_extra_preset
.setCurrentIndex(-1)
534 self
.input_extra_preset
.setEditable(True)
535 self
.input_extra_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
536 self
.input_extra_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
537 self
.input_extra_preset
.setPlaceholderText("Select a preset")
538 input_extra_preset_btn
=QPushButton("Insert")
539 input_extra_preset_btn
.clicked
.connect(self
.insert_preset_extra
)
540 layout_info2
.addWidget(self
.input_extra_preset
, 5)
541 layout_info2
.addWidget(input_extra_preset_btn
, 1)
542 layout_info
.addRow("Certify\nPreset", layout_info2
)
543 layout_info
.addRow("Certify /\nExtra", self
.input_extra
)
544 self
.input_mode
=QComboBox(self
)
545 self
.input_mode
.addItems(["In-Person", "Tele-Consultation", "Other"])
546 self
.input_mode
.setEditable(True)
547 layout_info
.addRow("Mode", self
.input_mode
)
548 self
.input_daw
=QCheckBox("Dispense as written", self
)
549 layout_info
.addRow("DAW", self
.input_daw
)
551 tab_note
=QWidget(self
)
552 layout_note
=QVBoxLayout(tab_note
)
553 layout_note2
=QHBoxLayout()
554 label_note
=QLabel("Clinical Notes")
555 self
.input_note_preset
=QComboBox(self
)
556 self
.input_note_preset
.addItems(self
.preset_note
.data
.keys())
557 self
.input_note_preset
.setCurrentIndex(-1)
558 self
.input_note_preset
.setEditable(True)
559 self
.input_note_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
560 self
.input_note_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
561 self
.input_note_preset
.setPlaceholderText("Select a preset")
562 input_note_preset_btn
=QPushButton("Insert")
563 input_note_preset_btn
.clicked
.connect(self
.insert_preset_note
)
564 layout_note2
.addWidget(self
.input_note_preset
, 5)
565 layout_note2
.addWidget(input_note_preset_btn
, 1)
566 self
.input_note
=QTextEdit(self
)
567 layout_note
.addWidget(label_note
)
568 layout_note
.addLayout(layout_note2
)
569 layout_note
.addWidget(self
.input_note
)
571 tab_report
=QWidget(self
)
572 layout_report
=QVBoxLayout(tab_report
)
573 layout_report2
=QHBoxLayout()
574 label_report
=QLabel("Available Reports")
575 self
.input_report_preset
=QComboBox(self
)
576 self
.input_report_preset
.addItems(self
.preset_report
.data
.keys())
577 self
.input_report_preset
.setCurrentIndex(-1)
578 self
.input_report_preset
.setEditable(True)
579 self
.input_report_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
580 self
.input_report_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
581 self
.input_report_preset
.setPlaceholderText("Select a preset")
582 input_report_preset_btn
=QPushButton("Insert")
583 input_report_preset_btn
.clicked
.connect(self
.insert_preset_report
)
584 layout_report2
.addWidget(self
.input_report_preset
, 5)
585 layout_report2
.addWidget(input_report_preset_btn
, 1)
586 self
.input_report
=QTextEdit(self
)
587 layout_report
.addWidget(label_report
)
588 layout_report
.addLayout(layout_report2
)
589 layout_report
.addWidget(self
.input_report
)
591 tab_advice
=QWidget(self
)
592 layout_advice
=QVBoxLayout(tab_advice
)
593 layout_advice2
=QHBoxLayout()
594 label_advice
=QLabel("Advice")
595 self
.input_advice_preset
=QComboBox(self
)
596 self
.input_advice_preset
.addItems(self
.preset_advice
.data
.keys())
597 self
.input_advice_preset
.setCurrentIndex(-1)
598 self
.input_advice_preset
.setEditable(True)
599 self
.input_advice_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
600 self
.input_advice_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
601 self
.input_advice_preset
.setPlaceholderText("Select a preset")
602 input_advice_preset_btn
=QPushButton("Insert")
603 input_advice_preset_btn
.clicked
.connect(self
.insert_preset_advice
)
604 layout_advice2
.addWidget(self
.input_advice_preset
, 5)
605 layout_advice2
.addWidget(input_advice_preset_btn
, 1)
606 self
.input_advice
=QTextEdit(self
)
607 layout_advice
.addWidget(label_advice
)
608 layout_advice
.addLayout(layout_advice2
)
609 layout_advice
.addWidget(self
.input_advice
)
611 tab_investigation
=QWidget(self
)
612 layout_investigation
=QVBoxLayout(tab_investigation
)
613 layout_investigation2
=QHBoxLayout()
614 label_investigation
=QLabel("Recommended Investigations")
615 self
.input_investigation_preset
=QComboBox(self
)
616 self
.input_investigation_preset
.addItems(self
.preset_investigation
.data
.keys())
617 self
.input_investigation_preset
.setCurrentIndex(-1)
618 self
.input_investigation_preset
.setEditable(True)
619 self
.input_investigation_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
620 self
.input_investigation_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
621 self
.input_investigation_preset
.setPlaceholderText("Select a preset")
622 input_investigation_preset_btn
=QPushButton("Insert")
623 input_investigation_preset_btn
.clicked
.connect(self
.insert_preset_investigation
)
624 layout_investigation2
.addWidget(self
.input_investigation_preset
, 5)
625 layout_investigation2
.addWidget(input_investigation_preset_btn
, 1)
626 self
.input_investigation
=QTextEdit(self
)
627 layout_investigation
.addWidget(label_investigation
)
628 layout_investigation
.addLayout(layout_investigation2
)
629 layout_investigation
.addWidget(self
.input_investigation
)
631 tab_medication
=QWidget(self
)
632 layout_medication
=QVBoxLayout(tab_medication
)
633 layout_medication2
=QHBoxLayout()
634 label_medication
=QLabel("Medication Advice")
635 self
.input_medication_preset
=QComboBox(self
)
636 self
.input_medication_preset
.addItems(self
.preset_medication
.data
.keys())
637 self
.input_medication_preset
.setCurrentIndex(-1)
638 self
.input_medication_preset
.setEditable(True)
639 self
.input_medication_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
640 self
.input_medication_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
641 self
.input_medication_preset
.setPlaceholderText("Select a preset")
642 input_medication_preset_btn
=QPushButton("Insert")
643 input_medication_preset_btn
.clicked
.connect(self
.insert_preset_medication
)
644 layout_medication2
.addWidget(self
.input_medication_preset
, 5)
645 layout_medication2
.addWidget(input_medication_preset_btn
, 1)
646 self
.input_medication
=QTextEdit(self
)
647 layout_medication
.addWidget(label_medication
)
648 layout_medication
.addLayout(layout_medication2
)
649 layout_medication
.addWidget(self
.input_medication
)
651 tab_additional
=QWidget(self
)
652 layout_additional
=QVBoxLayout(tab_additional
)
653 layout_additional2
=QHBoxLayout()
654 label_additional
=QLabel("Additional Advice")
655 self
.input_additional_preset
=QComboBox(self
)
656 self
.input_additional_preset
.addItems(self
.preset_additional
.data
.keys())
657 self
.input_additional_preset
.setCurrentIndex(-1)
658 self
.input_additional_preset
.setEditable(True)
659 self
.input_additional_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
660 self
.input_additional_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
661 self
.input_additional_preset
.setPlaceholderText("Select a preset")
662 input_additional_preset_btn
=QPushButton("Insert")
663 input_additional_preset_btn
.clicked
.connect(self
.insert_preset_additional
)
664 layout_additional2
.addWidget(self
.input_additional_preset
, 5)
665 layout_additional2
.addWidget(input_additional_preset_btn
, 1)
666 self
.input_additional
=QTextEdit(self
)
667 layout_additional
.addWidget(label_additional
)
668 layout_additional
.addLayout(layout_additional2
)
669 layout_additional
.addWidget(self
.input_additional
)
671 tab_attachment
=QWidget(self
)
672 layout_attachment
=QVBoxLayout(tab_attachment
)
673 layout_attachment2
=QHBoxLayout()
674 label_attachment
=QLabel("Attached files")
675 self
.input_attachment
=QListWidget(self
)
676 button_add
=QPushButton("Add")
677 button_add
.clicked
.connect(self
.add_attachment
)
678 button_remove
=QPushButton("Remove")
679 button_remove
.clicked
.connect(self
.remove_attachment
)
680 button_save
=QPushButton("Save")
681 button_save
.clicked
.connect(self
.save_attachment
)
682 layout_attachment
.addWidget(label_attachment
)
683 layout_attachment
.addLayout(layout_attachment2
)
684 layout_attachment
.addWidget(self
.input_attachment
)
685 layout_attachment2
.addWidget(button_add
)
686 layout_attachment2
.addWidget(button_remove
)
687 layout_attachment2
.addWidget(button_save
)
690 tab
.addTab(tab_info
, "Patient")
691 tab
.addTab(tab_note
, "Clinical")
692 tab
.addTab(tab_report
, "Report")
693 tab
.addTab(tab_advice
, "Advice")
694 tab
.addTab(tab_investigation
, "Investigation")
695 tab
.addTab(tab_medication
, "Medication")
696 tab
.addTab(tab_additional
, "Additional")
697 tab
.addTab(tab_attachment
, "Attachment")
699 self
.setCentralWidget(tab
)
701 self
.statusbar
=QStatusBar()
702 self
.setStatusBar(self
.statusbar
)
704 self
.renderbox
=RenderBox()
705 self
.signal_view
.connect(self
.renderbox
.update
)
706 self
.edit_configuration
=EditConfiguration()
707 self
.edit_prescriber
=EditPrescriber()
708 self
.edit_prescriber
.signal_save
.connect(self
.cmd_prescriber_reload
)
709 self
.viewbox
=ViewBox()
712 if(config
["filename"]):
713 self
.cmd_open(config
["filename"])
715 if(len(self
.prescription
.prescriber
.name
.strip())<1):
716 self
.cmd_prescriber()
718 self
.setWindowIcon(QIcon(os
.path
.join("resource", "icon_medscript.ico")))