Ejemplo n.º 1
0
    def __init__(self, parent = None):
        super(ReceptionPage, self).__init__(parent)

        self.notes_widget = NotesWidget(self)
        self.notes_widget.set_type(NotesWidget.RECEPTION)

        self.menu_bar = QtGui.QToolBar()
        self.menu_bar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)

        ## TODO placeholder icon
        icon = QtGui.QIcon.fromTheme("dialog-question")
        self.action_payment = QtGui.QAction(icon, "Take Payment", self)
        self.menu_bar.addAction(self.action_payment)

        right_widget =  QtGui.QLabel("Placeholder", self)
        right_widget.setMinimumWidth(100)
        right_widget.setMaximumWidth(150)

        self.pt_diary_widget = PtDiaryWidget()

        layout = QtGui.QGridLayout(self)
        layout.setMargin(3)
        layout.setSpacing(3)
        layout.addWidget(self.notes_widget, 0, 0)
        layout.addWidget(right_widget, 0, 1)
        layout.addWidget(self.pt_diary_widget, 1, 0, 1, 2)
        layout.addWidget(self.menu_bar, 2, 0, 1, 2)

        self.connect_signals()
Ejemplo n.º 2
0
class ReceptionPage(QtGui.QWidget):
    def __init__(self, parent = None):
        super(ReceptionPage, self).__init__(parent)

        self.notes_widget = NotesWidget(self)
        self.notes_widget.set_type(NotesWidget.RECEPTION)

        self.menu_bar = QtGui.QToolBar()
        self.menu_bar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)

        ## TODO placeholder icon
        icon = QtGui.QIcon.fromTheme("dialog-question")
        self.action_payment = QtGui.QAction(icon, "Take Payment", self)
        self.menu_bar.addAction(self.action_payment)

        right_widget =  QtGui.QLabel("Placeholder", self)
        right_widget.setMinimumWidth(100)
        right_widget.setMaximumWidth(150)

        self.pt_diary_widget = PtDiaryWidget()

        layout = QtGui.QGridLayout(self)
        layout.setMargin(3)
        layout.setSpacing(3)
        layout.addWidget(self.notes_widget, 0, 0)
        layout.addWidget(right_widget, 0, 1)
        layout.addWidget(self.pt_diary_widget, 1, 0, 1, 2)
        layout.addWidget(self.menu_bar, 2, 0, 1, 2)

        self.connect_signals()

    def Advise(self, *args):
        if __name__ == "__main__":
            print args
        self.emit(QtCore.SIGNAL("Advise"), *args)

    def connect_signals(self):
        self.action_payment.triggered.connect(self.payment_action)

    def clear(self):
        self.notes_widget.clear()
        self.pt_diary_widget.clear()

    def load_patient(self):
        patient = SETTINGS.current_patient
        self.notes_widget.load_patient()
        self.pt_diary_widget.load_patient()

    def payment_action(self):
        print "todo payment"
        self.Advise("take payment")

    def send_save_request(self):
        print "todo save clerical note"
        self.emit(QtCore.SIGNAL("Save Requested"))
Ejemplo n.º 3
0
    def __init__(self, parent = None):
        QtGui.QWidget.__init__(self, parent)

        self._proc_code_dock_widget = None #initialise if needed.

        self.control_panel = ControlPanel(self)
        '''a pointer to the :doc:`ControlPanel`'''

        self.details_browser = DetailsBrowser(self)
        '''a pointer to the :doc:`DetailsBrowser`'''

        self.reception_page = ReceptionPage(self)
        '''a pointer to the :doc:`ReceptionPage`'''

        self.charts_page = ChartsPage(self)
        '''a pointer to the :doc:`ChartsPage`'''

        self.treatment_page = TreatmentPage(self)
        '''a pointer to the :doc:`TreatmentPage`'''

        self.notes_page = NotesWidget(self)
        '''a pointer to the :doc:`NotesWidget`'''

        self.estimates_page = EstimatesPage(self)
        '''a pointer to the :doc:`EstimatesPage`'''

        self.history_page = HistoryPage(self)
        '''a pointer to the :doc:`HistoryPage`'''

        # summary_page shares the "model" of the static chart
        model = self.charts_page.static.chart_data_model
        self.summary_page = SummaryPage(model, self)
        '''a pointer to the :doc:`SummaryPage`'''

        self.options_widget = PatientInterfaceOptionsWidget(self)
        '''a pointer to the :doc:`PatientInterfaceOptionsWidget`'''

        self.options_widget.tab_index_changed(0)

        self.tab_widget = QtGui.QTabWidget(self)
        '''a pointer to the TabWidget'''
        self.tab_widget.tabBar().setFocusPolicy(QtCore.Qt.NoFocus)
        self.tab_widget.addTab(self.reception_page, _("Reception"))
        self.tab_widget.addTab(self.summary_page, _("Summary"))
        self.tab_widget.addTab(self.charts_page, _("Charts"))
        self.tab_widget.addTab(self.treatment_page, _("Treatment"))
        self.tab_widget.addTab(self.estimates_page, _("Estimates"))
        self.tab_widget.addTab(self.notes_page, _("Notes"))
        self.tab_widget.addTab(self.history_page, _("History"))
        self.tab_widget.setCornerWidget(self.options_widget)

        left_widget = QtGui.QWidget()
        left_widget.setMaximumWidth(240)
        layout = QtGui.QVBoxLayout(left_widget)
        layout.setMargin(0)
        layout.addWidget(self.control_panel)
        layout.addWidget(self.details_browser)

        layout = QtGui.QHBoxLayout(self)
        layout.addWidget(left_widget)
        layout.addWidget(self.tab_widget)

        ## keep this dialog everpresent..
        ## it can then keep a record of previous searches etc...
        self._find_dialog = None

        self.load_history, self.history_pos = [], -1
        self.pt = None
        #self.clear()
        self.connect_signals()
Ejemplo n.º 4
0
class PatientInterface(QtGui.QWidget):
    '''
    PatientInterface
    A composite widget containing all elements for viewing a patient record
    '''
    def __init__(self, parent = None):
        QtGui.QWidget.__init__(self, parent)

        self._proc_code_dock_widget = None #initialise if needed.

        self.control_panel = ControlPanel(self)
        '''a pointer to the :doc:`ControlPanel`'''

        self.details_browser = DetailsBrowser(self)
        '''a pointer to the :doc:`DetailsBrowser`'''

        self.reception_page = ReceptionPage(self)
        '''a pointer to the :doc:`ReceptionPage`'''

        self.charts_page = ChartsPage(self)
        '''a pointer to the :doc:`ChartsPage`'''

        self.treatment_page = TreatmentPage(self)
        '''a pointer to the :doc:`TreatmentPage`'''

        self.notes_page = NotesWidget(self)
        '''a pointer to the :doc:`NotesWidget`'''

        self.estimates_page = EstimatesPage(self)
        '''a pointer to the :doc:`EstimatesPage`'''

        self.history_page = HistoryPage(self)
        '''a pointer to the :doc:`HistoryPage`'''

        # summary_page shares the "model" of the static chart
        model = self.charts_page.static.chart_data_model
        self.summary_page = SummaryPage(model, self)
        '''a pointer to the :doc:`SummaryPage`'''

        self.options_widget = PatientInterfaceOptionsWidget(self)
        '''a pointer to the :doc:`PatientInterfaceOptionsWidget`'''

        self.options_widget.tab_index_changed(0)

        self.tab_widget = QtGui.QTabWidget(self)
        '''a pointer to the TabWidget'''
        self.tab_widget.tabBar().setFocusPolicy(QtCore.Qt.NoFocus)
        self.tab_widget.addTab(self.reception_page, _("Reception"))
        self.tab_widget.addTab(self.summary_page, _("Summary"))
        self.tab_widget.addTab(self.charts_page, _("Charts"))
        self.tab_widget.addTab(self.treatment_page, _("Treatment"))
        self.tab_widget.addTab(self.estimates_page, _("Estimates"))
        self.tab_widget.addTab(self.notes_page, _("Notes"))
        self.tab_widget.addTab(self.history_page, _("History"))
        self.tab_widget.setCornerWidget(self.options_widget)

        left_widget = QtGui.QWidget()
        left_widget.setMaximumWidth(240)
        layout = QtGui.QVBoxLayout(left_widget)
        layout.setMargin(0)
        layout.addWidget(self.control_panel)
        layout.addWidget(self.details_browser)

        layout = QtGui.QHBoxLayout(self)
        layout.addWidget(left_widget)
        layout.addWidget(self.tab_widget)

        ## keep this dialog everpresent..
        ## it can then keep a record of previous searches etc...
        self._find_dialog = None

        self.load_history, self.history_pos = [], -1
        self.pt = None
        #self.clear()
        self.connect_signals()

    @property
    def is_dirty(self):
        return self.ok_to_leave_record()

    @property
    def find_dialog(self):
        if self._find_dialog is None:
            self._find_dialog = dialogs.FindPatientDialog(self)
        return self._find_dialog

    def connect_signals(self):

        app = QtGui.QApplication.instance()

        self.control_panel.refresh_clicked.connect(self.reload_patient)
        self.control_panel.new_patient_clicked.connect(self.new_patient)
        self.control_panel.next_patient_clicked.connect(self.next_patient)
        self.control_panel.last_patient_clicked.connect(self.last_patient)
        self.control_panel.related_patient_clicked.connect(self.related_patient)
        self.control_panel.find_patient_clicked.connect(self.find_patient)
        self.control_panel.home_clicked.connect(self.go_home)

        self.connect(self.find_dialog, QtCore.SIGNAL("Advise"), self.Advise)
        self.connect(self.find_dialog, QtCore.SIGNAL("Load Serial Number"),
            self.load_patient)

        self.connect(self.treatment_page, QtCore.SIGNAL("Advise"), self.Advise)
        self.connect(self.summary_page, QtCore.SIGNAL("Advise"), self.Advise)

        self.connect(app, QtCore.SIGNAL("proc code selected"),
            self.treatment_page.proc_code_selected)

        self.connect(app, QtCore.SIGNAL("treatment item generated"),
            self.treatment_page.add_treatment_item)

        self.connect(self.details_browser,
            QtCore.SIGNAL("Edit Patient Details"), self.edit_patient)
        self.connect(self.details_browser,
            QtCore.SIGNAL("Edit Patient Address"), self.edit_address)
        self.connect(self.details_browser,
            QtCore.SIGNAL("Edit Patient Phone"), self.edit_phone)

        self.connect(self.summary_page.summary_chart,
            QtCore.SIGNAL("key_press"),
            self.charts_page.tooth_data_editor.keyPressEvent)

        self.connect(self.summary_page.summary_chart,
            QtCore.SIGNAL("current tooth changed"),
            self.charts_page.sync_static)

        self.connect(self.charts_page.static,
            QtCore.SIGNAL("current tooth changed"),
            self.summary_page.sync_static)

        self.connect(self.charts_page,
            QtCore.SIGNAL("clear summary chart selection"),
            self.summary_page.clear_static)

        self.connect(self.summary_page.summary_chart, QtCore.SIGNAL("Focused"),
            self.charts_page.tooth_data_editor.set_mode)

        self.connect(self.charts_page, QtCore.SIGNAL("teeth present changed"),
            self.summary_page.summary_chart.set_known_teeth)

        self.connect(self.charts_page, QtCore.SIGNAL("add treatment"),
            self.treatment_page.chart_treatment_added)

        self.connect(self.treatment_page, QtCore.SIGNAL("garbage chart tx"),
            self.charts_page.tooth_data_editor.invalid_input)

        self.connect(self.treatment_page, QtCore.SIGNAL("valid chart tx"),
            self.charts_page.tooth_data_editor.valid_input)

        self.connect(self.summary_page.summary_chart,
            QtCore.SIGNAL("teeth present changed"),
            self.charts_page.known_teeth_changed)

        self.connect(self.charts_page,
            QtCore.SIGNAL("update patient teeth present"),
            self.update_patient_teeth_present)

        for page in (self.charts_page, self.treatment_page, self.summary_page):
            self.connect(page, QtCore.SIGNAL("Show Fee Widget"),
                self.call_fee_widget)

        self.connect(self.summary_page, QtCore.SIGNAL("clinical_memo_changed"),
            self.update_patient_memo_clinical)

        self.connect(self.details_browser,
            QtCore.SIGNAL("Edit clerical memo"),
            self.update_patient_memo_clerical)

        self.tab_widget.currentChanged.connect(self.tab_index_changed)

        self.connect(self.options_widget,
            QtCore.SIGNAL("chart style"), self.set_chart_style)

        self.connect(self.summary_page.bpe_widget,
            QtCore.SIGNAL("New BPE"), self.new_bpe)

        self.connect(self.summary_page.bpe_widget,
            QtCore.SIGNAL("Show BPE"), self.list_bpes)

    def Advise(self, *args):
        if __name__ == "__main__":
            print args
        self.emit(QtCore.SIGNAL("Advise"), *args)

    def clear(self):
        '''
        clears all displayed information by iterating over child widgets
        and calling their clear functions
        '''
        self.details_browser.clear()
        self.apply_mode()

        for i in range(self.tab_widget.count()):
            self.tab_widget.widget(i).clear()
        ## not sure why I have to call these again??
        self.summary_page.clear()
        self.options_widget.clear()
        self.notes_page.clear()

        self.pt = None
        self.emit(QtCore.SIGNAL("Patient Loaded"), None)

    def apply_mode(self):
        '''
        applies the chosen "mode" which can be either a surgery machine or
        reception machine
        '''
        mode = SETTINGS.PERSISTANT_SETTINGS.get("mode", 0)
        self.tab_widget.setCurrentIndex(mode)
        self.options_widget.tab_index_changed(mode)

    def tab_index_changed(self, i):
        self.options_widget.tab_index_changed(i)
        page = self.tab_widget.widget(i)
        if page == self.notes_page:
            self.notes_page.load_patient()
        elif page == self.treatment_page:
            self.treatment_page.load_patient()
        elif page == self.history_page:
            self.history_page.load_patient()

    def edit_patient(self):
        '''
        functionality for editing parameters stored in the patients table
        for the loaded patient
        '''
        pt = self.pt['patient']
        dl = dialogs.EditPatientDialog(pt, self)
        if dl.exec_():
            dl.apply()
            if pt.is_dirty:
                if pt.commit_changes():
                    self.refresh_patient()

    def edit_address(self, index):
        '''
        functionality for editing and adding address information
        for the loaded patient
        '''
        address_db = self.pt['addresses']
        dl = dialogs.AddressDialog(address_db, index, self)
        refresh_needed = False
        if dl.exec_():
            self.Advise("update addresses!")
            self.refresh_patient()
        else:
            self.Advise("addresses unchanged!")

    def edit_phone(self):
        self.Advise("edit phone numbers")

    def call_fee_widget(self):
        if self._proc_code_dock_widget is not None:
            state =  self.proc_code_dock_widget.isVisible()
            self.proc_code_dock_widget.setVisible(not state)
            return

        self.emit(QtCore.SIGNAL("Show Fee Widget"), self.proc_code_dock_widget)

    @property
    def proc_code_dock_widget(self):
        if self._proc_code_dock_widget is None:
            self._proc_code_dock_widget = ProcCodeDockWidget(self)
            self.connect(self._proc_code_dock_widget,
                QtCore.SIGNAL("Code Selected"),
                self.treatment_page.proc_code_selected)
        return self._proc_code_dock_widget

    def update_patient_teeth_present(self, key):
        if not self.pt:
            return
        self.pt.set_dent_key(key)

    def update_patient_memo_clinical(self, memo):
        if not self.pt:
            return
        self.pt["memo_clinical"].setValue("memo", memo)

    def update_patient_memo_clerical(self):
        if not self.pt:
            return
        dl = QtGui.QInputDialog(self)
        dl.setWindowTitle(_("Edit Memo"))
        dl.setLabelText(_("Edit Memo"))
        dl.setTextValue(self.pt.clerical_memo)
        dl.resize(QtCore.QSize(400,200))
        if dl.exec_():
            self.pt["memo_clerical"].setValue("memo", dl.textValue())
            self.details_browser.setHtml(self.pt.details_html())

    def set_chart_style(self, enum):
        for chart in (
            self.summary_page.summary_chart,
            self.charts_page.static,
            self.charts_page.treatment,
            self.charts_page.completed):
            chart.setStyle(enum)

    def new_bpe(self):
        '''
        raises a dialog and creates a new bpe record for the current patient
        '''
        if not self.pt:
            return
        dl = dialogs.NewBpeDialog(self)
        if dl.exec_():
            self.pt.refresh_bpe()
            self.summary_page.bpe_widget.set_values(self.pt.current_bpe)

    def list_bpes(self):
        '''
        raises a dialog showing all bpes for the current patient
        '''
        dl = dialogs.ListBpeDialog(self)
        dl.exec_()

    def new_patient(self):
        '''
        raises a dialog and creates a new patient record in the database
        '''
        dl = dialogs.NewPatientDialog(self)
        self.connect(dl, QtCore.SIGNAL("Advise"), self.Advise)
        self.connect(dl, QtCore.SIGNAL("Load Serial Number"),
                self.load_patient)
        dl.exec_()

    def next_patient(self):
        '''
        cycle forwards through the list of recently visited records
        '''
        self.history_pos += 1
        try:
            self.load_patient(self.load_history[self.history_pos], True)
        except IndexError:
            self.history_pos = len(self.load_history) - 1
            self.Advise(_("Reached end of the List"))

    def last_patient(self):
        '''
        cycle backwards through recently visited records
        '''
        self.history_pos -= 1
        try:
            self.load_patient(self.load_history[self.history_pos], True)
        except IndexError:
            self.history_pos = 0
            self.Advise(_("Reached Start of the List"))

    def related_patient(self):
        self.Advise("related patient")

    def find_patient(self):
        '''
        raise a dialog which allows for patient search
        I keep a pointer to this dialog at all times
        (ie,it is not garbage collected)
        when the dialog wants a patient loaded it communicates via a signal
        '''
        if self.find_dialog.exec_():
            self.find_dialog.apply()

    def go_home(self):
        '''
        user has called for a clearing of the current record
        '''
        if not self.ok_to_leave_record():
            return

        self.clear()

    def reload_patient(self):
        '''
        reload from the database (has record been altered by another terminal?)
        '''
        if self.pt:
            self.load_patient(self.pt.patient_id, True)

    def refresh_patient(self):
        '''
        refresh after internal (possibly non-comitted) changes
        '''
        if self.pt:
            self._load_patient()

    def _load_patient(self):
        '''
        updates the ui after a pt load from db, or a dialog box change
        '''
        SETTINGS.set_current_patient(self.pt)
        self.details_browser.setHtml(self.pt.details_html())
        self.options_widget.clear()
        self.history_page.clear()
        self.estimates_page.clear()
        for page in (
            self.reception_page,
            self.summary_page,
            self.charts_page,
            self.treatment_page,
            ):
                page.load_patient()

        self.summary_page.summary_chart.chart_data_model.endResetModel()
        self.pt.treatment_model.update_views()
        self.apply_mode()

    def load_patient(self, patient_id, called_via_history = False):
        '''
        load patient with id patient_id
        if optional 2nd arg is passed, this means don't alter the history list
        '''
        if not self.ok_to_leave_record():
            return
        self.clear()

        self.Advise(u"%s<br />%d"% (_("Loading Record Number"), patient_id))
        QtGui.QApplication.instance().setOverrideCursor(QtCore.Qt.WaitCursor)

        if __name__ == "__main__":
            exception_ = db_orm.PatientNotFoundError
        else:
            exception_ = Exception

        try:
            self.pt = db_orm.PatientModel(patient_id)
            self._load_patient()
            self.emit(QtCore.SIGNAL("Patient Loaded"), self.pt)
            if not called_via_history:
                self.load_history.append(patient_id)
                self.history_pos = len(self.load_history) - 1
        except db_orm.PatientNotFoundError:
            self.Advise(
            u"%s %d %s"% (_("Record"), patient_id, _("not found!")), 1)
        except exception_ as e:
            QtGui.QApplication.instance().restoreOverrideCursor()
            exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()

            readable_ex =   traceback.format_exception(
                exceptionType, exceptionValue, exceptionTraceback)

            message = u"%s\n\n"% _("Unhandled exception - please file a bug")

            for line in readable_ex:
                message += "%s\n" % line
            self.Advise(message, 2)


        QtGui.QApplication.instance().restoreOverrideCursor()

    #@property
    def ok_to_leave_record(self):
        LOGGER.debug("checking state before leaving record")
        if self.pt is None:
            return True
        LOGGER.debug("pt is not none")
        self.update_patient()
        LOGGER.debug("updated patient charts")
        if self.pt.is_dirty:
            result = self.save_patient(True)
            LOGGER.debug("save patient - %s"% result)
            if not result:
                return False
        return True

    def update_patient(self):
        '''
        applies any changes from the underlying pages into the db ORM
        '''
        self.charts_page.update_patient()

    def save_patient(self, closing=False):
        if self.pt is None:
            return
        self.update_patient()
        if self.pt.is_dirty:
            message = "You have unsaved changes to record %s"% (
                self.pt.patient_id)

            dl = dialogs.SaveDiscardCancelDialog(message,
                self.pt.what_has_changed, self)
            dl.discard_but.setVisible(closing)
            if dl.exec_():
                if dl.save_on_exit:
                    self.pt.commit_changes()
                return True
            else:
                return False
        else:
            self.Advise(_("No changes Found"))
Ejemplo n.º 5
0
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self._proc_code_dock_widget = None  #initialise if needed.

        self.control_panel = ControlPanel(self)
        '''a pointer to the :doc:`ControlPanel`'''

        self.details_browser = DetailsBrowser(self)
        '''a pointer to the :doc:`DetailsBrowser`'''

        self.reception_page = ReceptionPage(self)
        '''a pointer to the :doc:`ReceptionPage`'''

        self.charts_page = ChartsPage(self)
        '''a pointer to the :doc:`ChartsPage`'''

        self.treatment_page = TreatmentPage(self)
        '''a pointer to the :doc:`TreatmentPage`'''

        self.notes_page = NotesWidget(self)
        '''a pointer to the :doc:`NotesWidget`'''

        self.estimates_page = EstimatesPage(self)
        '''a pointer to the :doc:`EstimatesPage`'''

        self.history_page = HistoryPage(self)
        '''a pointer to the :doc:`HistoryPage`'''

        # summary_page shares the "model" of the static chart
        model = self.charts_page.static.chart_data_model
        self.summary_page = SummaryPage(model, self)
        '''a pointer to the :doc:`SummaryPage`'''

        self.options_widget = PatientInterfaceOptionsWidget(self)
        '''a pointer to the :doc:`PatientInterfaceOptionsWidget`'''

        self.options_widget.tab_index_changed(0)

        self.tab_widget = QtGui.QTabWidget(self)
        '''a pointer to the TabWidget'''
        self.tab_widget.tabBar().setFocusPolicy(QtCore.Qt.NoFocus)
        self.tab_widget.addTab(self.reception_page, _("Reception"))
        self.tab_widget.addTab(self.summary_page, _("Summary"))
        self.tab_widget.addTab(self.charts_page, _("Charts"))
        self.tab_widget.addTab(self.treatment_page, _("Treatment"))
        self.tab_widget.addTab(self.estimates_page, _("Estimates"))
        self.tab_widget.addTab(self.notes_page, _("Notes"))
        self.tab_widget.addTab(self.history_page, _("History"))
        self.tab_widget.setCornerWidget(self.options_widget)

        left_widget = QtGui.QWidget()
        left_widget.setMaximumWidth(240)
        layout = QtGui.QVBoxLayout(left_widget)
        layout.setMargin(0)
        layout.addWidget(self.control_panel)
        layout.addWidget(self.details_browser)

        layout = QtGui.QHBoxLayout(self)
        layout.addWidget(left_widget)
        layout.addWidget(self.tab_widget)

        ## keep this dialog everpresent..
        ## it can then keep a record of previous searches etc...
        self._find_dialog = None

        self.load_history, self.history_pos = [], -1
        self.pt = None
        #self.clear()
        self.connect_signals()
Ejemplo n.º 6
0
class PatientInterface(QtGui.QWidget):
    '''
    PatientInterface
    A composite widget containing all elements for viewing a patient record
    '''
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self._proc_code_dock_widget = None  #initialise if needed.

        self.control_panel = ControlPanel(self)
        '''a pointer to the :doc:`ControlPanel`'''

        self.details_browser = DetailsBrowser(self)
        '''a pointer to the :doc:`DetailsBrowser`'''

        self.reception_page = ReceptionPage(self)
        '''a pointer to the :doc:`ReceptionPage`'''

        self.charts_page = ChartsPage(self)
        '''a pointer to the :doc:`ChartsPage`'''

        self.treatment_page = TreatmentPage(self)
        '''a pointer to the :doc:`TreatmentPage`'''

        self.notes_page = NotesWidget(self)
        '''a pointer to the :doc:`NotesWidget`'''

        self.estimates_page = EstimatesPage(self)
        '''a pointer to the :doc:`EstimatesPage`'''

        self.history_page = HistoryPage(self)
        '''a pointer to the :doc:`HistoryPage`'''

        # summary_page shares the "model" of the static chart
        model = self.charts_page.static.chart_data_model
        self.summary_page = SummaryPage(model, self)
        '''a pointer to the :doc:`SummaryPage`'''

        self.options_widget = PatientInterfaceOptionsWidget(self)
        '''a pointer to the :doc:`PatientInterfaceOptionsWidget`'''

        self.options_widget.tab_index_changed(0)

        self.tab_widget = QtGui.QTabWidget(self)
        '''a pointer to the TabWidget'''
        self.tab_widget.tabBar().setFocusPolicy(QtCore.Qt.NoFocus)
        self.tab_widget.addTab(self.reception_page, _("Reception"))
        self.tab_widget.addTab(self.summary_page, _("Summary"))
        self.tab_widget.addTab(self.charts_page, _("Charts"))
        self.tab_widget.addTab(self.treatment_page, _("Treatment"))
        self.tab_widget.addTab(self.estimates_page, _("Estimates"))
        self.tab_widget.addTab(self.notes_page, _("Notes"))
        self.tab_widget.addTab(self.history_page, _("History"))
        self.tab_widget.setCornerWidget(self.options_widget)

        left_widget = QtGui.QWidget()
        left_widget.setMaximumWidth(240)
        layout = QtGui.QVBoxLayout(left_widget)
        layout.setMargin(0)
        layout.addWidget(self.control_panel)
        layout.addWidget(self.details_browser)

        layout = QtGui.QHBoxLayout(self)
        layout.addWidget(left_widget)
        layout.addWidget(self.tab_widget)

        ## keep this dialog everpresent..
        ## it can then keep a record of previous searches etc...
        self._find_dialog = None

        self.load_history, self.history_pos = [], -1
        self.pt = None
        #self.clear()
        self.connect_signals()

    @property
    def is_dirty(self):
        return self.ok_to_leave_record()

    @property
    def find_dialog(self):
        if self._find_dialog is None:
            self._find_dialog = dialogs.FindPatientDialog(self)
        return self._find_dialog

    def connect_signals(self):

        app = QtGui.QApplication.instance()

        self.control_panel.refresh_clicked.connect(self.reload_patient)
        self.control_panel.new_patient_clicked.connect(self.new_patient)
        self.control_panel.next_patient_clicked.connect(self.next_patient)
        self.control_panel.last_patient_clicked.connect(self.last_patient)
        self.control_panel.related_patient_clicked.connect(
            self.related_patient)
        self.control_panel.find_patient_clicked.connect(self.find_patient)
        self.control_panel.home_clicked.connect(self.go_home)

        self.connect(self.find_dialog, QtCore.SIGNAL("Advise"), self.Advise)
        self.connect(self.find_dialog, QtCore.SIGNAL("Load Serial Number"),
                     self.load_patient)

        self.connect(self.treatment_page, QtCore.SIGNAL("Advise"), self.Advise)
        self.connect(self.summary_page, QtCore.SIGNAL("Advise"), self.Advise)

        self.connect(app, QtCore.SIGNAL("proc code selected"),
                     self.treatment_page.proc_code_selected)

        self.connect(app, QtCore.SIGNAL("treatment item generated"),
                     self.treatment_page.add_treatment_item)

        self.connect(self.details_browser,
                     QtCore.SIGNAL("Edit Patient Details"), self.edit_patient)
        self.connect(self.details_browser,
                     QtCore.SIGNAL("Edit Patient Address"), self.edit_address)
        self.connect(self.details_browser, QtCore.SIGNAL("Edit Patient Phone"),
                     self.edit_phone)

        self.connect(self.summary_page.summary_chart,
                     QtCore.SIGNAL("key_press"),
                     self.charts_page.tooth_data_editor.keyPressEvent)

        self.connect(self.summary_page.summary_chart,
                     QtCore.SIGNAL("current tooth changed"),
                     self.charts_page.sync_static)

        self.connect(self.charts_page.static,
                     QtCore.SIGNAL("current tooth changed"),
                     self.summary_page.sync_static)

        self.connect(self.charts_page,
                     QtCore.SIGNAL("clear summary chart selection"),
                     self.summary_page.clear_static)

        self.connect(self.summary_page.summary_chart, QtCore.SIGNAL("Focused"),
                     self.charts_page.tooth_data_editor.set_mode)

        self.connect(self.charts_page, QtCore.SIGNAL("teeth present changed"),
                     self.summary_page.summary_chart.set_known_teeth)

        self.connect(self.charts_page, QtCore.SIGNAL("add treatment"),
                     self.treatment_page.chart_treatment_added)

        self.connect(self.treatment_page, QtCore.SIGNAL("garbage chart tx"),
                     self.charts_page.tooth_data_editor.invalid_input)

        self.connect(self.treatment_page, QtCore.SIGNAL("valid chart tx"),
                     self.charts_page.tooth_data_editor.valid_input)

        self.connect(self.summary_page.summary_chart,
                     QtCore.SIGNAL("teeth present changed"),
                     self.charts_page.known_teeth_changed)

        self.connect(self.charts_page,
                     QtCore.SIGNAL("update patient teeth present"),
                     self.update_patient_teeth_present)

        for page in (self.charts_page, self.treatment_page, self.summary_page):
            self.connect(page, QtCore.SIGNAL("Show Fee Widget"),
                         self.call_fee_widget)

        self.connect(self.summary_page, QtCore.SIGNAL("clinical_memo_changed"),
                     self.update_patient_memo_clinical)

        self.connect(self.details_browser, QtCore.SIGNAL("Edit clerical memo"),
                     self.update_patient_memo_clerical)

        self.tab_widget.currentChanged.connect(self.tab_index_changed)

        self.connect(self.options_widget, QtCore.SIGNAL("chart style"),
                     self.set_chart_style)

        self.connect(self.summary_page.bpe_widget, QtCore.SIGNAL("New BPE"),
                     self.new_bpe)

        self.connect(self.summary_page.bpe_widget, QtCore.SIGNAL("Show BPE"),
                     self.list_bpes)

    def Advise(self, *args):
        if __name__ == "__main__":
            print args
        self.emit(QtCore.SIGNAL("Advise"), *args)

    def clear(self):
        '''
        clears all displayed information by iterating over child widgets
        and calling their clear functions
        '''
        self.details_browser.clear()
        self.apply_mode()

        for i in range(self.tab_widget.count()):
            self.tab_widget.widget(i).clear()
        ## not sure why I have to call these again??
        self.summary_page.clear()
        self.options_widget.clear()
        self.notes_page.clear()

        self.pt = None
        self.emit(QtCore.SIGNAL("Patient Loaded"), None)

    def apply_mode(self):
        '''
        applies the chosen "mode" which can be either a surgery machine or
        reception machine
        '''
        mode = SETTINGS.PERSISTANT_SETTINGS.get("mode", 0)
        self.tab_widget.setCurrentIndex(mode)
        self.options_widget.tab_index_changed(mode)

    def tab_index_changed(self, i):
        self.options_widget.tab_index_changed(i)
        page = self.tab_widget.widget(i)
        if page == self.notes_page:
            self.notes_page.load_patient()
        elif page == self.treatment_page:
            self.treatment_page.load_patient()
        elif page == self.history_page:
            self.history_page.load_patient()

    def edit_patient(self):
        '''
        functionality for editing parameters stored in the patients table
        for the loaded patient
        '''
        pt = self.pt['patient']
        dl = dialogs.EditPatientDialog(pt, self)
        if dl.exec_():
            dl.apply()
            if pt.is_dirty:
                if pt.commit_changes():
                    self.refresh_patient()

    def edit_address(self, index):
        '''
        functionality for editing and adding address information
        for the loaded patient
        '''
        address_db = self.pt['addresses']
        dl = dialogs.AddressDialog(address_db, index, self)
        refresh_needed = False
        if dl.exec_():
            self.Advise("update addresses!")
            self.refresh_patient()
        else:
            self.Advise("addresses unchanged!")

    def edit_phone(self):
        self.Advise("edit phone numbers")

    def call_fee_widget(self):
        if self._proc_code_dock_widget is not None:
            state = self.proc_code_dock_widget.isVisible()
            self.proc_code_dock_widget.setVisible(not state)
            return

        self.emit(QtCore.SIGNAL("Show Fee Widget"), self.proc_code_dock_widget)

    @property
    def proc_code_dock_widget(self):
        if self._proc_code_dock_widget is None:
            self._proc_code_dock_widget = ProcCodeDockWidget(self)
            self.connect(self._proc_code_dock_widget,
                         QtCore.SIGNAL("Code Selected"),
                         self.treatment_page.proc_code_selected)
        return self._proc_code_dock_widget

    def update_patient_teeth_present(self, key):
        if not self.pt:
            return
        self.pt.set_dent_key(key)

    def update_patient_memo_clinical(self, memo):
        if not self.pt:
            return
        self.pt["memo_clinical"].setValue("memo", memo)

    def update_patient_memo_clerical(self):
        if not self.pt:
            return
        dl = QtGui.QInputDialog(self)
        dl.setWindowTitle(_("Edit Memo"))
        dl.setLabelText(_("Edit Memo"))
        dl.setTextValue(self.pt.clerical_memo)
        dl.resize(QtCore.QSize(400, 200))
        if dl.exec_():
            self.pt["memo_clerical"].setValue("memo", dl.textValue())
            self.details_browser.setHtml(self.pt.details_html())

    def set_chart_style(self, enum):
        for chart in (self.summary_page.summary_chart, self.charts_page.static,
                      self.charts_page.treatment, self.charts_page.completed):
            chart.setStyle(enum)

    def new_bpe(self):
        '''
        raises a dialog and creates a new bpe record for the current patient
        '''
        if not self.pt:
            return
        dl = dialogs.NewBpeDialog(self)
        if dl.exec_():
            self.pt.refresh_bpe()
            self.summary_page.bpe_widget.set_values(self.pt.current_bpe)

    def list_bpes(self):
        '''
        raises a dialog showing all bpes for the current patient
        '''
        dl = dialogs.ListBpeDialog(self)
        dl.exec_()

    def new_patient(self):
        '''
        raises a dialog and creates a new patient record in the database
        '''
        dl = dialogs.NewPatientDialog(self)
        self.connect(dl, QtCore.SIGNAL("Advise"), self.Advise)
        self.connect(dl, QtCore.SIGNAL("Load Serial Number"),
                     self.load_patient)
        dl.exec_()

    def next_patient(self):
        '''
        cycle forwards through the list of recently visited records
        '''
        self.history_pos += 1
        try:
            self.load_patient(self.load_history[self.history_pos], True)
        except IndexError:
            self.history_pos = len(self.load_history) - 1
            self.Advise(_("Reached end of the List"))

    def last_patient(self):
        '''
        cycle backwards through recently visited records
        '''
        self.history_pos -= 1
        try:
            self.load_patient(self.load_history[self.history_pos], True)
        except IndexError:
            self.history_pos = 0
            self.Advise(_("Reached Start of the List"))

    def related_patient(self):
        self.Advise("related patient")

    def find_patient(self):
        '''
        raise a dialog which allows for patient search
        I keep a pointer to this dialog at all times
        (ie,it is not garbage collected)
        when the dialog wants a patient loaded it communicates via a signal
        '''
        if self.find_dialog.exec_():
            self.find_dialog.apply()

    def go_home(self):
        '''
        user has called for a clearing of the current record
        '''
        if not self.ok_to_leave_record():
            return

        self.clear()

    def reload_patient(self):
        '''
        reload from the database (has record been altered by another terminal?)
        '''
        if self.pt:
            self.load_patient(self.pt.patient_id, True)

    def refresh_patient(self):
        '''
        refresh after internal (possibly non-comitted) changes
        '''
        if self.pt:
            self._load_patient()

    def _load_patient(self):
        '''
        updates the ui after a pt load from db, or a dialog box change
        '''
        SETTINGS.set_current_patient(self.pt)
        self.details_browser.setHtml(self.pt.details_html())
        self.options_widget.clear()
        self.history_page.clear()
        self.estimates_page.clear()
        for page in (
                self.reception_page,
                self.summary_page,
                self.charts_page,
                self.treatment_page,
        ):
            page.load_patient()

        self.summary_page.summary_chart.chart_data_model.endResetModel()
        self.pt.treatment_model.update_views()
        self.apply_mode()

    def load_patient(self, patient_id, called_via_history=False):
        '''
        load patient with id patient_id
        if optional 2nd arg is passed, this means don't alter the history list
        '''
        if not self.ok_to_leave_record():
            return
        self.clear()

        self.Advise(u"%s<br />%d" % (_("Loading Record Number"), patient_id))
        QtGui.QApplication.instance().setOverrideCursor(QtCore.Qt.WaitCursor)

        if __name__ == "__main__":
            exception_ = db_orm.PatientNotFoundError
        else:
            exception_ = Exception

        try:
            self.pt = db_orm.PatientModel(patient_id)
            self._load_patient()
            self.emit(QtCore.SIGNAL("Patient Loaded"), self.pt)
            if not called_via_history:
                self.load_history.append(patient_id)
                self.history_pos = len(self.load_history) - 1
        except db_orm.PatientNotFoundError:
            self.Advise(
                u"%s %d %s" % (_("Record"), patient_id, _("not found!")), 1)
        except exception_ as e:
            QtGui.QApplication.instance().restoreOverrideCursor()
            exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()

            readable_ex = traceback.format_exception(exceptionType,
                                                     exceptionValue,
                                                     exceptionTraceback)

            message = u"%s\n\n" % _("Unhandled exception - please file a bug")

            for line in readable_ex:
                message += "%s\n" % line
            self.Advise(message, 2)

        QtGui.QApplication.instance().restoreOverrideCursor()

    #@property
    def ok_to_leave_record(self):
        LOGGER.debug("checking state before leaving record")
        if self.pt is None:
            return True
        LOGGER.debug("pt is not none")
        self.update_patient()
        LOGGER.debug("updated patient charts")
        if self.pt.is_dirty:
            result = self.save_patient(True)
            LOGGER.debug("save patient - %s" % result)
            if not result:
                return False
        return True

    def update_patient(self):
        '''
        applies any changes from the underlying pages into the db ORM
        '''
        self.charts_page.update_patient()

    def save_patient(self, closing=False):
        if self.pt is None:
            return
        self.update_patient()
        if self.pt.is_dirty:
            message = "You have unsaved changes to record %s" % (
                self.pt.patient_id)

            dl = dialogs.SaveDiscardCancelDialog(message,
                                                 self.pt.what_has_changed,
                                                 self)
            dl.discard_but.setVisible(closing)
            if dl.exec_():
                if dl.save_on_exit:
                    self.pt.commit_changes()
                return True
            else:
                return False
        else:
            self.Advise(_("No changes Found"))