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
, real_dir
, 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 year
=datetime
.datetime
.now().year
186 copy
="2023"+"-"+str(year
)
189 txt
="<h1>MedScript</h1>"
190 txt
=txt
+"<p>Version 0.2</p>"
191 txt
=txt
+"<p>The Prescription Writing Software</p>"
192 txt
=txt
+"<p><a href='https://code.agnibho.com/medscript/'>Website</a></p>"
193 txt
=txt
+"<p>Copyright © "+copy
+" Dr. Agnibho Mondal</p>"
194 QMessageBox
.about(self
,"MedScript", txt
)
197 self
.viewbox
.md(os
.path
.join(real_dir
, "README"))
200 def insert_preset_extra(self
):
202 self
.input_extra
.insertPlainText(self
.preset_extra
.data
[self
.input_extra_preset
.currentText()])
204 self
.input_extra
.insertPlainText(self
.input_extra_preset
.currentText())
206 self
.input_extra_preset
.setCurrentIndex(-1)
207 if config
["preset_newline"]:
208 self
.input_extra
.insertPlainText("\n")
210 def insert_preset_note(self
):
212 self
.input_note
.insertPlainText(self
.preset_note
.data
[self
.input_note_preset
.currentText()])
214 self
.input_note
.insertPlainText(self
.input_note_preset
.currentText())
216 self
.input_note_preset
.setCurrentIndex(-1)
217 if config
["preset_newline"]:
218 self
.input_note
.insertPlainText("\n")
220 def insert_preset_report(self
):
222 self
.input_report
.insertPlainText(self
.preset_report
.data
[self
.input_report_preset
.currentText()])
224 self
.input_report
.insertPlainText(self
.input_report_preset
.currentText())
226 self
.input_report_preset
.setCurrentIndex(-1)
227 if config
["preset_newline"]:
228 self
.input_report
.insertPlainText("\n")
229 def insert_preset_advice(self
):
231 self
.input_advice
.insertPlainText(self
.preset_advice
.data
[self
.input_advice_preset
.currentText()])
233 self
.input_advice
.insertPlainText(self
.input_advice_preset
.currentText())
235 self
.input_advice_preset
.setCurrentIndex(-1)
236 if config
["preset_newline"]:
237 self
.input_advice
.insertPlainText("\n")
240 def insert_preset_investigation(self
):
242 self
.input_investigation
.insertPlainText(self
.preset_investigation
.data
[self
.input_investigation_preset
.currentText()])
244 self
.input_investigation
.insertPlainText(self
.input_investigation_preset
.currentText())
246 self
.input_investigation_preset
.setCurrentIndex(-1)
247 if config
["preset_newline"]:
248 self
.input_investigation
.insertPlainText("\n")
250 def insert_preset_medication(self
):
252 self
.input_medication
.insertPlainText(self
.preset_medication
.data
[self
.input_medication_preset
.currentText()])
254 self
.input_medication
.insertPlainText(self
.input_medication_preset
.currentText())
256 self
.input_medication_preset
.setCurrentIndex(-1)
257 if config
["preset_newline"]:
258 self
.input_medication
.insertPlainText("\n")
260 def insert_preset_additional(self
):
262 self
.input_additional
.insertPlainText(self
.preset_additional
.data
[self
.input_additional_preset
.currentText()])
264 self
.input_additional
.insertPlainText(self
.input_additional_preset
.currentText())
266 self
.input_additional_preset
.setCurrentIndex(-1)
267 if config
["preset_newline"]:
268 self
.input_additional
.insertPlainText("\n")
270 def load_interface(self
, file="", date
=None, id="", name
="", age
="", sex
="", address
="", contact
="", extra
="", mode
="", daw
="", diagnosis
="", note
="", report
="", advice
="", investigation
="", medication
="", additional
=""):
272 file_msg
=self
.current_file
.file if self
.current_file
.file else "New file"
273 sign_msg
="(signed)" if sign_available
and config
["smime"] and self
.current_file
.is_signed() else ""
274 self
.statusbar
.showMessage(file_msg
+" "+sign_msg
)
276 d
=QDateTime
.currentDateTime()
279 pdate
=dateutil
.parser
.parse(date
)
280 d
=QDateTime
.fromString(pdate
.strftime("%Y-%m-%d %H:%M:%S"), "yyyy-MM-dd hh:mm:ss")
281 except Exception as e
:
282 QMessageBox
.warning(self
,"Failed to load", str(e
))
284 self
.input_date
.setDateTime(d
)
285 self
.input_id
.setText(id)
286 self
.input_name
.setText(name
)
287 self
.input_age
.setText(age
)
288 self
.input_sex
.setCurrentText(sex
)
289 self
.input_address
.setText(address
)
290 self
.input_contact
.setText(contact
)
291 self
.input_extra
.setText(extra
)
292 self
.input_mode
.setCurrentText(mode
)
293 self
.input_daw
.setChecked(bool(daw
))
294 self
.input_diagnosis
.setText(diagnosis
)
295 self
.input_note
.setText(note
)
296 self
.input_report
.setText(report
)
297 self
.input_advice
.setText(advice
)
298 self
.input_investigation
.setText(investigation
)
299 self
.input_medication
.setText(medication
)
300 self
.input_additional
.setText(additional
)
301 self
.label_prescriber
.setText(self
.prescription
.prescriber
.name
)
302 except Exception as e
:
303 QMessageBox
.warning(self
,"Failed to load", "Failed to load the data into the application.")
306 def load_interface_from_instance(self
):
308 file=self
.prescription
.file,
309 date
=self
.prescription
.date
,
310 id=self
.prescription
.id,
311 name
=self
.prescription
.name
,
312 age
=self
.prescription
.age
,
313 sex
=self
.prescription
.sex
,
314 address
=self
.prescription
.address
,
315 contact
=self
.prescription
.contact
,
316 extra
=self
.prescription
.extra
,
317 mode
=self
.prescription
.mode
,
318 daw
=self
.prescription
.daw
,
319 diagnosis
=self
.prescription
.diagnosis
,
320 note
=self
.prescription
.note
,
321 report
=self
.prescription
.report
,
322 advice
=self
.prescription
.advice
,
323 investigation
=self
.prescription
.investigation
,
324 medication
=self
.prescription
.medication
,
325 additional
=self
.prescription
.additional
328 def update_instance(self
):
330 self
.prescription
.set_data(
331 date
=self
.input_date
.dateTime().toString("yyyy-MM-dd hh:mm:ss"),
332 id=self
.input_id
.text(),
333 name
=self
.input_name
.text(),
334 age
=self
.input_age
.text(),
335 sex
=self
.input_sex
.currentText(),
336 address
=self
.input_address
.text(),
337 contact
=self
.input_contact
.text(),
338 extra
=self
.input_extra
.toPlainText(),
339 mode
=self
.input_mode
.currentText(),
340 daw
=self
.input_daw
.isChecked(),
341 diagnosis
=self
.input_diagnosis
.text(),
342 note
=self
.input_note
.toPlainText(),
343 report
=self
.input_report
.toPlainText(),
344 advice
=self
.input_advice
.toPlainText(),
345 investigation
=self
.input_investigation
.toPlainText(),
346 medication
=self
.input_medication
.toPlainText(),
347 additional
=self
.input_additional
.toPlainText()
349 except Exception as e
:
350 QMessageBox
.critical(self
,"Failed", "Critical failure happned. Please check console for more info.")
354 self
.current_file
.reset()
355 self
.prescription
.set_data()
356 self
.input_attachment
.clear()
357 self
.load_interface()
358 self
.update_instance()
359 self
.save_state
=md5(self
.prescription
.get_json().encode()).hexdigest()
362 self
.update_instance()
363 self
.load_interface_from_instance()
365 def add_attachment(self
):
367 new
=QFileDialog
.getOpenFileName(self
, "Open File", config
["document_directory"], "PDF (*.pdf);; Images (*.jpg, *.jpeg, *.png, *.gif);; All Files (*)")[0]
369 self
.input_attachment
.addItem(new
)
370 except Exception as e
:
371 QMessageBox
.warning(self
,"Attach failed", "Failed to attach file.")
374 def remove_attachment(self
):
375 index
=self
.input_attachment
.currentRow()
377 self
.current_file
.delete_attachment(self
.input_attachment
.item(index
).text())
378 self
.input_attachment
.takeItem(index
)
380 QMessageBox
.warning(self
, "Select item", "Please select an attachment to remove.")
382 def save_attachment(self
):
384 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])
385 except Exception as e
:
388 def load_attachment(self
, attachments
):
389 for attach
in attachments
:
390 self
.input_attachment
.addItem(attach
)
392 def confirm_close(self
):
394 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?"))
397 def closeEvent(self
, event
):
398 if(self
.confirm_close()):
403 def __init__(self
, *args
, **kwargs
):
404 super().__init
__(*args
, **kwargs
)
406 global sign_available
408 self
.setWindowTitle("MedScript")
409 self
.setGeometry(100, 100, 600, 400)
410 self
.setWindowIcon(QIcon(os
.path
.join(config
["resource"], "icon_medscript.ico")))
412 icon_open
=QIcon(os
.path
.join(config
["resource"], "icon_open.svg"))
413 icon_save
=QIcon(os
.path
.join(config
["resource"], "icon_save.svg"))
414 icon_render
=QIcon(os
.path
.join(config
["resource"], "icon_render.svg"))
415 icon_refresh
=QIcon(os
.path
.join(config
["resource"], "icon_refresh.svg"))
417 self
.preset_extra
=Preset(os
.path
.join(config
["preset_directory"], "certify.csv"))
418 self
.preset_note
=Preset(os
.path
.join(config
["preset_directory"], "note.csv"))
419 self
.preset_report
=Preset(os
.path
.join(config
["preset_directory"], "report.csv"))
420 self
.preset_advice
=Preset(os
.path
.join(config
["preset_directory"], "advice.csv"))
421 self
.preset_investigation
=Preset(os
.path
.join(config
["preset_directory"], "investigation.csv"))
422 self
.preset_medication
=Preset(os
.path
.join(config
["preset_directory"], "medication.csv"), text_as_key
=True)
423 self
.preset_additional
=Preset(os
.path
.join(config
["preset_directory"], "additional.csv"))
425 action_new
=QAction("New", self
)
426 action_new
.setShortcut("Ctrl+N")
427 action_new
.triggered
.connect(self
.cmd_new
)
428 action_open
=QAction("Open", self
)
429 action_open2
=QAction(icon_open
, "Open", self
)
430 action_open
.setShortcut("Ctrl+O")
431 action_open
.triggered
.connect(self
.cmd_open
)
432 action_open2
.triggered
.connect(self
.cmd_open
)
433 action_save
=QAction("Save", self
)
434 action_save2
=QAction(icon_save
, "Save", self
)
435 action_save
.setShortcut("Ctrl+S")
436 action_save
.triggered
.connect(self
.cmd_save
)
437 action_save2
.triggered
.connect(self
.cmd_save
)
438 action_save_as
=QAction("Save As", self
)
439 action_save_as
.setShortcut("Ctrl+Shift+S")
440 action_save_as
.triggered
.connect(self
.cmd_save_as
)
441 action_refresh
=QAction("Refresh", self
)
442 action_refresh
.setShortcut("F5")
443 action_refresh2
=QAction(icon_refresh
, "Refresh", self
)
444 action_refresh
.triggered
.connect(self
.cmd_refresh
)
445 action_refresh2
.triggered
.connect(self
.cmd_refresh
)
446 action_quit
=QAction("Quit", self
)
447 action_quit
.setShortcut("Ctrl+Q")
448 action_quit
.triggered
.connect(self
.cmd_quit
)
449 action_render
=QAction("Render", self
)
450 action_render
.setShortcut("Ctrl+R")
451 action_render2
=QAction(icon_render
, "Render", self
)
452 action_render
.triggered
.connect(self
.cmd_render
)
453 action_render2
.triggered
.connect(self
.cmd_render
)
454 action_sign
=QAction("Sign", self
)
455 action_sign
.triggered
.connect(self
.cmd_sign
)
456 action_unsign
=QAction("Unsign", self
)
457 action_unsign
.triggered
.connect(self
.cmd_unsign
)
458 action_verify
=QAction("Verify", self
)
459 action_verify
.triggered
.connect(self
.cmd_verify
)
460 action_configuration
=QAction("Configuration", self
)
461 action_configuration
.triggered
.connect(self
.cmd_configuration
)
462 action_prescriber
=QAction("Prescriber", self
)
463 action_prescriber
.triggered
.connect(self
.cmd_prescriber
)
464 action_switch
=QAction("Switch", self
)
465 action_switch
.triggered
.connect(self
.cmd_switch
)
466 action_about
=QAction("About", self
)
467 action_about
.triggered
.connect(self
.cmd_about
)
468 action_help
=QAction("Help", self
)
469 action_help
.setShortcut("F1")
470 action_help
.triggered
.connect(self
.cmd_help
)
472 menubar
=self
.menuBar()
473 menu_file
=menubar
.addMenu("File")
474 menu_file
.addAction(action_new
)
475 menu_file
.addAction(action_open
)
476 menu_file
.addAction(action_save
)
477 menu_file
.addAction(action_save_as
)
478 menu_file
.addAction(action_quit
)
479 menu_prepare
=menubar
.addMenu("Prepare")
480 menu_prepare
.addAction(action_render
)
481 menu_prepare
.addAction(action_refresh
)
482 if(sign_available
and config
["smime"]):
483 menu_prepare
.addAction(action_sign
)
484 menu_prepare
.addAction(action_unsign
)
485 menu_prepare
.addAction(action_verify
)
486 menu_settings
=menubar
.addMenu("Settings")
487 menu_settings
.addAction(action_configuration
)
488 menu_settings
.addAction(action_prescriber
)
489 menu_settings
.addAction(action_switch
)
490 menu_help
=menubar
.addMenu("Help")
491 menu_help
.addAction(action_about
)
492 menu_help
.addAction(action_help
)
494 toolbar
=QToolBar("Main Toolbar", floatable
=False, movable
=False)
495 toolbar
.setIconSize(QSize(16, 16))
496 toolbar
.addAction(action_open2
)
497 toolbar
.addAction(action_save2
)
498 toolbar
.addAction(action_refresh2
)
499 toolbar
.addAction(action_render2
)
500 toolbar
.addSeparator()
501 self
.input_template
=QComboBox(self
)
502 templates
=os
.listdir(config
["template_directory"])
504 templates
.remove(os
.path
.basename(config
["template"]))
505 templates
.insert(0, os
.path
.basename(config
["template"]))
506 except Exception as e
:
508 self
.input_template
.addItems(templates
)
509 toolbar
.addWidget(self
.input_template
)
511 spacer
.setSizePolicy(QSizePolicy
.Policy
.Expanding
, QSizePolicy
.Policy
.Expanding
)
512 toolbar
.addWidget(spacer
)
513 self
.label_prescriber
=QLabel(self
)
514 toolbar
.addWidget(self
.label_prescriber
)
515 self
.addToolBar(toolbar
)
517 tab_info
=QWidget(self
)
518 layout_info
=QFormLayout(tab_info
)
519 layout_info2
=QHBoxLayout()
520 self
.input_date
=QDateTimeEdit(self
)
521 self
.input_date
.setDisplayFormat("MMMM dd, yyyy hh:mm a")
522 layout_info
.addRow("Date", self
.input_date
)
523 self
.input_id
=QLineEdit(self
)
524 layout_info
.addRow("ID", self
.input_id
)
525 self
.input_name
=QLineEdit(self
)
526 layout_info
.addRow("Name", self
.input_name
)
527 self
.input_age
=QLineEdit(self
)
528 layout_info
.addRow("Age", self
.input_age
)
529 self
.input_sex
=QComboBox(self
)
530 self
.input_sex
.addItems(["Male", "Female", "Other"])
531 self
.input_sex
.setEditable(True)
532 layout_info
.addRow("Sex", self
.input_sex
)
533 self
.input_address
=QLineEdit(self
)
534 layout_info
.addRow("Address", self
.input_address
)
535 self
.input_contact
=QLineEdit(self
)
536 layout_info
.addRow("Contact", self
.input_contact
)
537 self
.input_diagnosis
=QLineEdit(self
)
538 layout_info
.addRow("Diagnosis", self
.input_diagnosis
)
539 self
.input_extra
=QTextEdit(self
)
540 self
.input_extra_preset
=QComboBox(self
)
541 self
.input_extra_preset
.addItems(self
.preset_extra
.data
.keys())
542 self
.input_extra_preset
.setCurrentIndex(-1)
543 self
.input_extra_preset
.setEditable(True)
544 self
.input_extra_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
545 self
.input_extra_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
546 self
.input_extra_preset
.setPlaceholderText("Select a preset")
547 input_extra_preset_btn
=QPushButton("Insert")
548 input_extra_preset_btn
.clicked
.connect(self
.insert_preset_extra
)
549 layout_info2
.addWidget(self
.input_extra_preset
, 5)
550 layout_info2
.addWidget(input_extra_preset_btn
, 1)
551 layout_info
.addRow("Certify\nPreset", layout_info2
)
552 layout_info
.addRow("Certify /\nExtra", self
.input_extra
)
553 self
.input_mode
=QComboBox(self
)
554 self
.input_mode
.addItems(["In-Person", "Tele-Consultation", "Other"])
555 self
.input_mode
.setEditable(True)
556 layout_info
.addRow("Mode", self
.input_mode
)
557 self
.input_daw
=QCheckBox("Dispense as written", self
)
558 layout_info
.addRow("DAW", self
.input_daw
)
560 tab_note
=QWidget(self
)
561 layout_note
=QVBoxLayout(tab_note
)
562 layout_note2
=QHBoxLayout()
563 label_note
=QLabel("Clinical Notes")
564 label_note
.setProperty("class", "info_head")
565 self
.input_note_preset
=QComboBox(self
)
566 self
.input_note_preset
.addItems(self
.preset_note
.data
.keys())
567 self
.input_note_preset
.setCurrentIndex(-1)
568 self
.input_note_preset
.setEditable(True)
569 self
.input_note_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
570 self
.input_note_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
571 self
.input_note_preset
.setPlaceholderText("Select a preset")
572 input_note_preset_btn
=QPushButton("Insert")
573 input_note_preset_btn
.clicked
.connect(self
.insert_preset_note
)
574 layout_note2
.addWidget(self
.input_note_preset
, 5)
575 layout_note2
.addWidget(input_note_preset_btn
, 1)
576 self
.input_note
=QTextEdit(self
)
577 layout_note
.addWidget(label_note
)
578 layout_note
.addLayout(layout_note2
)
579 layout_note
.addWidget(self
.input_note
)
581 tab_report
=QWidget(self
)
582 layout_report
=QVBoxLayout(tab_report
)
583 layout_report2
=QHBoxLayout()
584 label_report
=QLabel("Available Reports")
585 label_report
.setProperty("class", "info_head")
586 self
.input_report_preset
=QComboBox(self
)
587 self
.input_report_preset
.addItems(self
.preset_report
.data
.keys())
588 self
.input_report_preset
.setCurrentIndex(-1)
589 self
.input_report_preset
.setEditable(True)
590 self
.input_report_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
591 self
.input_report_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
592 self
.input_report_preset
.setPlaceholderText("Select a preset")
593 input_report_preset_btn
=QPushButton("Insert")
594 input_report_preset_btn
.clicked
.connect(self
.insert_preset_report
)
595 layout_report2
.addWidget(self
.input_report_preset
, 5)
596 layout_report2
.addWidget(input_report_preset_btn
, 1)
597 self
.input_report
=QTextEdit(self
)
598 layout_report
.addWidget(label_report
)
599 layout_report
.addLayout(layout_report2
)
600 layout_report
.addWidget(self
.input_report
)
602 tab_advice
=QWidget(self
)
603 layout_advice
=QVBoxLayout(tab_advice
)
604 layout_advice2
=QHBoxLayout()
605 label_advice
=QLabel("Advice")
606 label_advice
.setProperty("class", "info_head")
607 self
.input_advice_preset
=QComboBox(self
)
608 self
.input_advice_preset
.addItems(self
.preset_advice
.data
.keys())
609 self
.input_advice_preset
.setCurrentIndex(-1)
610 self
.input_advice_preset
.setEditable(True)
611 self
.input_advice_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
612 self
.input_advice_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
613 self
.input_advice_preset
.setPlaceholderText("Select a preset")
614 input_advice_preset_btn
=QPushButton("Insert")
615 input_advice_preset_btn
.clicked
.connect(self
.insert_preset_advice
)
616 layout_advice2
.addWidget(self
.input_advice_preset
, 5)
617 layout_advice2
.addWidget(input_advice_preset_btn
, 1)
618 self
.input_advice
=QTextEdit(self
)
619 layout_advice
.addWidget(label_advice
)
620 layout_advice
.addLayout(layout_advice2
)
621 layout_advice
.addWidget(self
.input_advice
)
623 tab_investigation
=QWidget(self
)
624 layout_investigation
=QVBoxLayout(tab_investigation
)
625 layout_investigation2
=QHBoxLayout()
626 label_investigation
=QLabel("Recommended Investigations")
627 label_investigation
.setProperty("class", "info_head")
628 self
.input_investigation_preset
=QComboBox(self
)
629 self
.input_investigation_preset
.addItems(self
.preset_investigation
.data
.keys())
630 self
.input_investigation_preset
.setCurrentIndex(-1)
631 self
.input_investigation_preset
.setEditable(True)
632 self
.input_investigation_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
633 self
.input_investigation_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
634 self
.input_investigation_preset
.setPlaceholderText("Select a preset")
635 input_investigation_preset_btn
=QPushButton("Insert")
636 input_investigation_preset_btn
.clicked
.connect(self
.insert_preset_investigation
)
637 layout_investigation2
.addWidget(self
.input_investigation_preset
, 5)
638 layout_investigation2
.addWidget(input_investigation_preset_btn
, 1)
639 self
.input_investigation
=QTextEdit(self
)
640 layout_investigation
.addWidget(label_investigation
)
641 layout_investigation
.addLayout(layout_investigation2
)
642 layout_investigation
.addWidget(self
.input_investigation
)
644 tab_medication
=QWidget(self
)
645 layout_medication
=QVBoxLayout(tab_medication
)
646 layout_medication2
=QHBoxLayout()
647 label_medication
=QLabel("Medication Advice")
648 label_medication
.setProperty("class", "info_head")
649 self
.input_medication_preset
=QComboBox(self
)
650 self
.input_medication_preset
.addItems(self
.preset_medication
.data
.keys())
651 self
.input_medication_preset
.setCurrentIndex(-1)
652 self
.input_medication_preset
.setEditable(True)
653 self
.input_medication_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
654 self
.input_medication_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
655 self
.input_medication_preset
.setPlaceholderText("Select a preset")
656 input_medication_preset_btn
=QPushButton("Insert")
657 input_medication_preset_btn
.clicked
.connect(self
.insert_preset_medication
)
658 layout_medication2
.addWidget(self
.input_medication_preset
, 5)
659 layout_medication2
.addWidget(input_medication_preset_btn
, 1)
660 self
.input_medication
=QTextEdit(self
)
661 layout_medication
.addWidget(label_medication
)
662 layout_medication
.addLayout(layout_medication2
)
663 layout_medication
.addWidget(self
.input_medication
)
665 tab_additional
=QWidget(self
)
666 layout_additional
=QVBoxLayout(tab_additional
)
667 layout_additional2
=QHBoxLayout()
668 label_additional
=QLabel("Additional Advice")
669 label_additional
.setProperty("class", "info_head")
670 self
.input_additional_preset
=QComboBox(self
)
671 self
.input_additional_preset
.addItems(self
.preset_additional
.data
.keys())
672 self
.input_additional_preset
.setCurrentIndex(-1)
673 self
.input_additional_preset
.setEditable(True)
674 self
.input_additional_preset
.completer().setCompletionMode(QCompleter
.CompletionMode
.PopupCompletion
)
675 self
.input_additional_preset
.completer().setFilterMode(Qt
.MatchFlag
.MatchContains
)
676 self
.input_additional_preset
.setPlaceholderText("Select a preset")
677 input_additional_preset_btn
=QPushButton("Insert")
678 input_additional_preset_btn
.clicked
.connect(self
.insert_preset_additional
)
679 layout_additional2
.addWidget(self
.input_additional_preset
, 5)
680 layout_additional2
.addWidget(input_additional_preset_btn
, 1)
681 self
.input_additional
=QTextEdit(self
)
682 layout_additional
.addWidget(label_additional
)
683 layout_additional
.addLayout(layout_additional2
)
684 layout_additional
.addWidget(self
.input_additional
)
686 tab_attachment
=QWidget(self
)
687 layout_attachment
=QVBoxLayout(tab_attachment
)
688 layout_attachment2
=QHBoxLayout()
689 label_attachment
=QLabel("Attached files")
690 label_attachment
.setProperty("class", "info_head")
691 self
.input_attachment
=QListWidget(self
)
692 button_add
=QPushButton("Add")
693 button_add
.clicked
.connect(self
.add_attachment
)
694 button_remove
=QPushButton("Remove")
695 button_remove
.clicked
.connect(self
.remove_attachment
)
696 button_save
=QPushButton("Save")
697 button_save
.clicked
.connect(self
.save_attachment
)
698 layout_attachment
.addWidget(label_attachment
)
699 layout_attachment
.addLayout(layout_attachment2
)
700 layout_attachment
.addWidget(self
.input_attachment
)
701 layout_attachment2
.addWidget(button_add
)
702 layout_attachment2
.addWidget(button_remove
)
703 layout_attachment2
.addWidget(button_save
)
706 tab
.addTab(tab_info
, "Patient")
707 tab
.addTab(tab_note
, "Clinical")
708 tab
.addTab(tab_report
, "Report")
709 tab
.addTab(tab_advice
, "Advice")
710 tab
.addTab(tab_investigation
, "Investigation")
711 tab
.addTab(tab_medication
, "Medication")
712 tab
.addTab(tab_additional
, "Additional")
713 tab
.addTab(tab_attachment
, "Attachment")
715 self
.setCentralWidget(tab
)
717 self
.statusbar
=QStatusBar()
718 self
.setStatusBar(self
.statusbar
)
720 self
.renderbox
=RenderBox()
721 self
.signal_view
.connect(self
.renderbox
.update
)
722 self
.edit_configuration
=EditConfiguration()
723 self
.edit_prescriber
=EditPrescriber()
724 self
.edit_prescriber
.signal_save
.connect(self
.cmd_prescriber_reload
)
725 self
.viewbox
=ViewBox()
728 if(config
["filename"]):
729 self
.cmd_open(config
["filename"])
731 if(len(self
.prescription
.prescriber
.name
.strip())<1):
732 self
.cmd_prescriber()
734 self
.setWindowIcon(QIcon(os
.path
.join(config
["resource"], "icon_medscript.ico")))