Example #1
1
class HydroprintGUI(myqt.DialogWindow):

    ConsoleSignal = QSignal(str)

    def __init__(self, datamanager, parent=None):
        super(HydroprintGUI, self).__init__(parent, maximize=True)

        self.__updateUI = True

        # Child widgets:

        self.dmngr = datamanager
        self.dmngr.wldsetChanged.connect(self.wldset_changed)
        self.dmngr.wxdsetChanged.connect(self.wxdset_changed)

        self.page_setup_win = PageSetupWin(self)
        self.page_setup_win.newPageSetupSent.connect(self.layout_changed)

        self.color_palette_win = ColorsSetupWin(self)
        self.color_palette_win.newColorSetupSent.connect(self.update_colors)

        # Memory path variable:

        self.save_fig_dir = self.workdir

        # Generate UI:

        self.__initUI__()

    def __initUI__(self):

        # ---- Toolbar

        self.btn_save = btn_save = QToolButtonNormal(icons.get_icon('save'))
        btn_save.setToolTip('Save the well hydrograph')

        # btn_draw is usefull for debugging purposes
        btn_draw = QToolButtonNormal(icons.get_icon('refresh'))
        btn_draw.setToolTip('Force a refresh of the well hydrograph')
        btn_draw.hide()

        self.btn_load_layout = QToolButtonNormal(
                icons.get_icon('load_graph_config'))
        self.btn_load_layout.setToolTip(
                "<p>Load graph layout for the current water level "
                " datafile if it exists</p>")
        self.btn_load_layout.clicked.connect(self.load_layout_isClicked)

        self.btn_save_layout = QToolButtonNormal(
                icons.get_icon('save_graph_config'))
        self.btn_save_layout.setToolTip('Save current graph layout')
        self.btn_save_layout.clicked.connect(self.save_layout_isClicked)

        btn_bestfit_waterlvl = QToolButtonNormal(icons.get_icon('fit_y'))
        btn_bestfit_waterlvl.setToolTip('Best fit the water level scale')

        btn_bestfit_time = QToolButtonNormal(icons.get_icon('fit_x'))
        btn_bestfit_time.setToolTip('Best fit the time scale')

        self.btn_page_setup = QToolButtonNormal(icons.get_icon('page_setup'))
        self.btn_page_setup.setToolTip('Show the page setup window')
        self.btn_page_setup.clicked.connect(self.page_setup_win.show)

        btn_color_pick = QToolButtonNormal(icons.get_icon('color_picker'))
        btn_color_pick.setToolTip('<p>Show a window to setup the color palette'
                                  ' used to draw the hydrograph</p.')
        btn_color_pick.clicked.connect(self.color_palette_win.show)

        self.btn_language = LangToolButton()
        self.btn_language.setToolTip(
            "Set the language of the text shown in the graph.")
        self.btn_language.sig_lang_changed.connect(self.layout_changed)
        self.btn_language.setIconSize(icons.get_iconsize('normal'))

        # ---- Zoom Panel

        btn_zoom_out = QToolButtonSmall(icons.get_icon('zoom_out'))
        btn_zoom_out.setToolTip('Zoom out (ctrl + mouse-wheel-down)')
        btn_zoom_out.clicked.connect(self.zoom_out)

        btn_zoom_in = QToolButtonSmall(icons.get_icon('zoom_in'))
        btn_zoom_in.setToolTip('Zoom in (ctrl + mouse-wheel-up)')
        btn_zoom_in.clicked.connect(self.zoom_in)

        self.zoom_disp = QSpinBox()
        self.zoom_disp.setAlignment(Qt.AlignCenter)
        self.zoom_disp.setButtonSymbols(QAbstractSpinBox.NoButtons)
        self.zoom_disp.setReadOnly(True)
        self.zoom_disp.setSuffix(' %')
        self.zoom_disp.setRange(0, 9999)
        self.zoom_disp.setValue(100)

        zoom_pan = myqt.QFrameLayout()
        zoom_pan.setSpacing(3)
        zoom_pan.addWidget(btn_zoom_out, 0, 0)
        zoom_pan.addWidget(btn_zoom_in, 0, 1)
        zoom_pan.addWidget(self.zoom_disp, 0, 2)

        # LAYOUT :

        btn_list = [btn_save, btn_draw,
                    self.btn_load_layout, self.btn_save_layout, VSep(),
                    btn_bestfit_waterlvl, btn_bestfit_time, VSep(),
                    self.btn_page_setup, btn_color_pick, self.btn_language,
                    VSep(), zoom_pan]

        subgrid_toolbar = QGridLayout()
        toolbar_widget = QWidget()

        row = 0
        for col, btn in enumerate(btn_list):
            subgrid_toolbar.addWidget(btn, row, col)

        subgrid_toolbar.setSpacing(5)
        subgrid_toolbar.setContentsMargins(0, 0, 0, 0)
        subgrid_toolbar.setColumnStretch(col + 1, 100)

        toolbar_widget.setLayout(subgrid_toolbar)

        # ---- LEFT PANEL

        # SubGrid Hydrograph Frame :

        self.hydrograph = hydrograph.Hydrograph()
        self.hydrograph_scrollarea = mplFigViewer.ImageViewer()
        self.hydrograph_scrollarea.zoomChanged.connect(self.zoom_disp.setValue)

        grid_hydrograph = QGridLayout()
        grid_hydrograph.addWidget(self.hydrograph_scrollarea, 0, 0)
        grid_hydrograph.setRowStretch(0, 500)
        grid_hydrograph.setColumnStretch(0, 500)
        grid_hydrograph.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)

        # ASSEMBLING SubGrids :

        grid_layout = QGridLayout()
        self.grid_layout_widget = QFrame()

        row = 0
        grid_layout.addWidget(toolbar_widget, row, 0)
        row += 1
        grid_layout.addLayout(grid_hydrograph, row, 0)

        grid_layout.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)
        grid_layout.setSpacing(5)
        grid_layout.setColumnStretch(0, 500)
        grid_layout.setRowStretch(1, 500)

        self.grid_layout_widget.setLayout(grid_layout)

        # ---- Right Panel

        self.tabscales = self.__init_scalesTabWidget__()

        self.right_panel = myqt.QFrameLayout()
        self.right_panel.addWidget(self.dmngr, 0, 0)
        self.right_panel.addWidget(self.tabscales, 1, 0)
        self.right_panel.setRowStretch(2, 100)

        self.right_panel.setSpacing(15)

        # ---- MAIN GRID

        mainGrid = QGridLayout()

        mainGrid.addWidget(self.grid_layout_widget, 0, 0)
        mainGrid.addWidget(VSep(), 0, 1)
        mainGrid.addWidget(self.right_panel, 0, 2)

        mainGrid.setContentsMargins(10, 10, 10, 10)  # (L, T, R, B)
        mainGrid.setSpacing(15)
        mainGrid.setColumnStretch(0, 500)
        mainGrid.setColumnMinimumWidth(2, 250)

        self.setLayout(mainGrid)

        # ---- EVENTS

        # Toolbox Layout :

        btn_bestfit_waterlvl.clicked.connect(self.best_fit_waterlvl)
        btn_bestfit_time.clicked.connect(self.best_fit_time)
        btn_draw.clicked.connect(self.draw_hydrograph)
        btn_save.clicked.connect(self.select_save_path)

        # Hydrograph Layout :

        self.Ptot_scale.valueChanged.connect(self.layout_changed)
        self.qweather_bin.currentIndexChanged.connect(self.layout_changed)

        # ---- Init Image

        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def __init_scalesTabWidget__(self):

        class QRowLayout(QGridLayout):
            def __init__(self, items, parent=None):
                super(QRowLayout, self).__init__(parent)

                for col, item in enumerate(items):
                    self.addWidget(item, 0, col)

                self.setContentsMargins(0, 0, 0, 0)
                self.setColumnStretch(0, 100)

        # ---- Time axis properties

        # Generate the widgets :

        self.date_start_widget = QDateEdit()
        self.date_start_widget.setDisplayFormat('01 / MM / yyyy')
        self.date_start_widget.setAlignment(Qt.AlignCenter)
        self.date_start_widget.dateChanged.connect(self.layout_changed)

        self.date_end_widget = QDateEdit()
        self.date_end_widget.setDisplayFormat('01 / MM / yyyy')
        self.date_end_widget.setAlignment(Qt.AlignCenter)
        self.date_end_widget.dateChanged.connect(self.layout_changed)

        self.time_scale_label = QComboBox()
        self.time_scale_label.setEditable(False)
        self.time_scale_label.setInsertPolicy(QComboBox.NoInsert)
        self.time_scale_label.addItems(['Month', 'Year'])
        self.time_scale_label.setCurrentIndex(0)
        self.time_scale_label.currentIndexChanged.connect(self.layout_changed)

        self.dateDispFreq_spinBox = QSpinBox()
        self.dateDispFreq_spinBox.setSingleStep(1)
        self.dateDispFreq_spinBox.setMinimum(1)
        self.dateDispFreq_spinBox.setMaximum(100)
        self.dateDispFreq_spinBox.setValue(
            self.hydrograph.date_labels_pattern)
        self.dateDispFreq_spinBox.setAlignment(Qt.AlignCenter)
        self.dateDispFreq_spinBox.setKeyboardTracking(False)
        self.dateDispFreq_spinBox.valueChanged.connect(self.layout_changed)

        # Setting up the layout :

        widget_time_scale = QFrame()
        widget_time_scale.setFrameStyle(0)
        grid_time_scale = QGridLayout()

        GRID = [[QLabel('From :'), self.date_start_widget],
                [QLabel('To :'), self.date_end_widget],
                [QLabel('Scale :'), self.time_scale_label],
                [QLabel('Date Disp. Pattern:'),
                 self.dateDispFreq_spinBox]]

        for i, ROW in enumerate(GRID):
            grid_time_scale.addLayout(QRowLayout(ROW), i, 1)

        grid_time_scale.setVerticalSpacing(5)
        grid_time_scale.setContentsMargins(10, 10, 10, 10)

        widget_time_scale.setLayout(grid_time_scale)

        # ----- Water level axis properties

        # Widget :

        self.waterlvl_scale = QDoubleSpinBox()
        self.waterlvl_scale.setSingleStep(0.05)
        self.waterlvl_scale.setMinimum(0.05)
        self.waterlvl_scale.setSuffix('  m')
        self.waterlvl_scale.setAlignment(Qt.AlignCenter)
        self.waterlvl_scale.setKeyboardTracking(False)
        self.waterlvl_scale.valueChanged.connect(self.layout_changed)
        self.waterlvl_scale.setFixedWidth(100)

        self.waterlvl_max = QDoubleSpinBox()
        self.waterlvl_max.setSingleStep(0.1)
        self.waterlvl_max.setSuffix('  m')
        self.waterlvl_max.setAlignment(Qt.AlignCenter)
        self.waterlvl_max.setMinimum(-1000)
        self.waterlvl_max.setMaximum(1000)
        self.waterlvl_max.setKeyboardTracking(False)
        self.waterlvl_max.valueChanged.connect(self.layout_changed)
        self.waterlvl_max.setFixedWidth(100)

        self.NZGridWL_spinBox = QSpinBox()
        self.NZGridWL_spinBox.setSingleStep(1)
        self.NZGridWL_spinBox.setMinimum(1)
        self.NZGridWL_spinBox.setMaximum(50)
        self.NZGridWL_spinBox.setValue(self.hydrograph.NZGrid)
        self.NZGridWL_spinBox.setAlignment(Qt.AlignCenter)
        self.NZGridWL_spinBox.setKeyboardTracking(False)
        self.NZGridWL_spinBox.valueChanged.connect(self.layout_changed)
        self.NZGridWL_spinBox.setFixedWidth(100)

        self.datum_widget = QComboBox()
        self.datum_widget.addItems(['Ground Surface', 'Sea Level'])
        self.datum_widget.currentIndexChanged.connect(self.layout_changed)

        # Layout :

        subgrid_WLScale = QGridLayout()

        GRID = [[QLabel('Minimum :'), self.waterlvl_max],
                [QLabel('Scale :'), self.waterlvl_scale],
                [QLabel('Grid Divisions :'), self.NZGridWL_spinBox],
                [QLabel('Datum :'), self.datum_widget]]

        for i, ROW in enumerate(GRID):
            subgrid_WLScale.addLayout(QRowLayout(ROW), i, 1)

        subgrid_WLScale.setVerticalSpacing(5)
        subgrid_WLScale.setContentsMargins(10, 10, 10, 10)  # (L, T, R, B)

        WLScale_widget = QFrame()
        WLScale_widget.setFrameStyle(0)
        WLScale_widget.setLayout(subgrid_WLScale)

        # ---- Weather Axis

        # Widgets :

        self.Ptot_scale = QSpinBox()
        self.Ptot_scale.setSingleStep(5)
        self.Ptot_scale.setMinimum(5)
        self.Ptot_scale.setMaximum(500)
        self.Ptot_scale.setValue(20)
        self.Ptot_scale.setSuffix('  mm')
        self.Ptot_scale.setAlignment(Qt.AlignCenter)

        self.qweather_bin = QComboBox()
        self.qweather_bin.setEditable(False)
        self.qweather_bin.setInsertPolicy(QComboBox.NoInsert)
        self.qweather_bin.addItems(['day', 'week', 'month'])
        self.qweather_bin.setCurrentIndex(1)

        # Layout :

        layout = QGridLayout()

        GRID = [[QLabel('Precip. Scale :'), self.Ptot_scale],
                [QLabel('Resampling :'), self.qweather_bin]]

        for i, row in enumerate(GRID):
            layout.addLayout(QRowLayout(row), i, 1)

        layout.setVerticalSpacing(5)
        layout.setContentsMargins(10, 10, 10, 10)  # (L,T,R,B)
        layout.setRowStretch(i+1, 100)

        widget_weather_scale = QFrame()
        widget_weather_scale.setFrameStyle(0)
        widget_weather_scale.setLayout(layout)

        # ---- ASSEMBLING TABS

        tabscales = QTabWidget()
        tabscales.addTab(widget_time_scale, 'Time')
        tabscales.addTab(WLScale_widget, 'Water Level')
        tabscales.addTab(widget_weather_scale, 'Weather')

        return tabscales

    @property
    def workdir(self):
        return self.dmngr.workdir

    # ---- Utilities

    def zoom_in(self):
        self.hydrograph_scrollarea.zoomIn()

    def zoom_out(self):
        self.hydrograph_scrollarea.zoomOut()

    def update_colors(self):
        self.hydrograph.update_colors()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    # ---- Datasets Handlers

    @property
    def wldset(self):
        return self.dmngr.get_current_wldset()

    @property
    def wxdset(self):
        return self.dmngr.get_current_wxdset()

    def wldset_changed(self):
        """Handle when the water level dataset of the datamanager changes."""
        if self.wldset is None:
            self.clear_hydrograph()
            return
        else:
            self.hydrograph.set_wldset(self.wldset)
            self.hydrograph.gluedf = self.wldset.get_glue_at(-1)

        # Load the manual measurements.

        fname = os.path.join(
            self.workdir, "Water Levels", 'waterlvl_manual_measurements')
        tmeas, wlmeas = load_waterlvl_measures(fname, self.wldset['Well'])
        self.wldset.set_wlmeas(tmeas, wlmeas)

        # Setup the layout of the hydrograph.

        layout = self.wldset.get_layout()
        if layout is not None:
            msg = ("Loading existing graph layout for well %s." %
                   self.wldset['Well'])
            print(msg)
            self.ConsoleSignal.emit('<font color=black>%s</font>' % msg)
            self.load_graph_layout(layout)
        else:
            print('No graph layout exists for well %s.' % self.wldset['Well'])
            # Fit Water Level in Layout :
            self.__updateUI = False
            self.best_fit_waterlvl()
            self.best_fit_time()
            if self.dmngr.set_closest_wxdset() is None:
                self.draw_hydrograph()
            self.__updateUI = True

    def wxdset_changed(self):
        """Handle when the weather dataset of the datamanager changes."""
        if self.wldset is None:
            self.clear_hydrograph()
        else:
            self.hydrograph.set_wxdset(self.wxdset)
            QCoreApplication.processEvents()
            self.draw_hydrograph()

    # ---- Draw Hydrograph Handlers
    def best_fit_waterlvl(self):
        wldset = self.dmngr.get_current_wldset()
        if wldset is not None:
            WLscale, WLmin = self.hydrograph.best_fit_waterlvl()
            self.waterlvl_scale.setValue(WLscale)
            self.waterlvl_max.setValue(WLmin)

    def best_fit_time(self):
        wldset = self.dmngr.get_current_wldset()
        if wldset is not None:
            date0, date1 = self.hydrograph.best_fit_time(wldset.xldates)
            self.date_start_widget.setDate(QDate(date0[0], date0[1], date0[2]))
            self.date_end_widget.setDate(QDate(date1[0], date1[1], date1[2]))

    @QSlot()
    def mrc_wl_changed(self):
        """
        Force a redraw of the MRC water levels after the results have
        changed for the dataset.
        """
        self.hydrograph.draw_mrc_wl()
        self.hydrograph.setup_legend()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    @QSlot(GLUEDataFrameBase)
    def glue_wl_changed(self, gluedf):
        """
        Force a redraw of the GLUE water levels after the results have
        changed for the dataset.
        """
        self.hydrograph.set_gluedf(gluedf)
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def layout_changed(self):
        """
        When an element of the graph layout is changed in the UI.
        """

        if self.__updateUI is False:
            return

        self.update_graph_layout_parameter()

        if self.hydrograph.isHydrographExists is False:
            return

        sender = self.sender()

        if sender == self.btn_language:
            self.hydrograph.draw_ylabels()
            self.hydrograph.setup_xticklabels()
            self.hydrograph.setup_legend()
        elif sender in [self.waterlvl_max, self.waterlvl_scale]:
            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.NZGridWL_spinBox:
            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.update_precip_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.Ptot_scale:
            self.hydrograph.update_precip_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.datum_widget:
            yoffset = int(self.wldset['Elevation']/self.hydrograph.WLscale)
            yoffset *= self.hydrograph.WLscale

            self.hydrograph.WLmin = (yoffset - self.hydrograph.WLmin)

            self.waterlvl_max.blockSignals(True)
            self.waterlvl_max.setValue(self.hydrograph.WLmin)
            self.waterlvl_max.blockSignals(False)

            # This is calculated so that trailing zeros in the altitude of the
            # well is not carried to the y axis labels, so that they remain a
            # int multiple of *WLscale*.

            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.draw_waterlvl()
            self.hydrograph.draw_ylabels()
        elif sender in [self.date_start_widget, self.date_end_widget]:
            self.hydrograph.set_time_scale()
            self.hydrograph.draw_weather()
            self.hydrograph.draw_figure_title()
        elif sender == self.dateDispFreq_spinBox:
            self.hydrograph.set_time_scale()
            self.hydrograph.setup_xticklabels()
        elif sender == self.page_setup_win:
            self.hydrograph.update_fig_size()
            # Implicitly call : set_margins()
            #                   draw_ylabels()
            #                   set_time_scale()
            #                   draw_figure_title
            self.hydrograph.draw_waterlvl()
            self.hydrograph.setup_legend()
        elif sender == self.qweather_bin:
            self.hydrograph.resample_bin()
            self.hydrograph.draw_weather()
            self.hydrograph.draw_ylabels()
        elif sender == self.time_scale_label:
            self.hydrograph.set_time_scale()
            self.hydrograph.draw_weather()
        else:
            print('No action for this widget yet.')

        # !!!! temporary fix until I can find a better solution !!!!

#        sender.blockSignals(True)
        if type(sender) in [QDoubleSpinBox, QSpinBox]:
            sender.setReadOnly(True)

        for i in range(10):
            QCoreApplication.processEvents()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)
        for i in range(10):
            QCoreApplication.processEvents()

        if type(sender) in [QDoubleSpinBox, QSpinBox]:
            sender.setReadOnly(False)
#        sender.blockSignals(False)

    def update_graph_layout_parameter(self):

        # language :

        self.hydrograph.language = self.btn_language.language

        # Scales :

        self.hydrograph.WLmin = self.waterlvl_max.value()
        self.hydrograph.WLscale = self.waterlvl_scale.value()
        self.hydrograph.RAINscale = self.Ptot_scale.value()
        self.hydrograph.NZGrid = self.NZGridWL_spinBox.value()

        # WL Datum :

        self.hydrograph.WLdatum = self.datum_widget.currentIndex()

        # Dates :

        self.hydrograph.datemode = self.time_scale_label.currentText()

        year = self.date_start_widget.date().year()
        month = self.date_start_widget.date().month()
        self.hydrograph.TIMEmin = xldate_from_date_tuple((year, month, 1), 0)

        year = self.date_end_widget.date().year()
        month = self.date_end_widget.date().month()
        self.hydrograph.TIMEmax = xldate_from_date_tuple((year, month, 1), 0)

        self.hydrograph.date_labels_pattern = self.dateDispFreq_spinBox.value()

        # Page Setup :

        self.hydrograph.fwidth = self.page_setup_win.pageSize[0]
        self.hydrograph.fheight = self.page_setup_win.pageSize[1]
        self.hydrograph.va_ratio = self.page_setup_win.va_ratio

        self.hydrograph.trend_line = self.page_setup_win.isTrendLine
        self.hydrograph.isLegend = self.page_setup_win.isLegend
        self.hydrograph.isGraphTitle = self.page_setup_win.isGraphTitle
        self.hydrograph.set_meteo_on(self.page_setup_win.is_meteo_on)
        self.hydrograph.set_glue_wl_on(self.page_setup_win.is_glue_wl_on)
        self.hydrograph.set_mrc_wl_on(self.page_setup_win.is_mrc_wl_on)
        self.hydrograph.set_figframe_lw(self.page_setup_win.figframe_lw)

        # Weather bins :

        self.hydrograph.bwidth_indx = self.qweather_bin.currentIndex()

    def clear_hydrograph(self):
        """Clear the hydrograph figure to show only a blank canvas."""
        self.hydrograph.clf()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def draw_hydrograph(self):
        if self.dmngr.wldataset_count() == 0:
            msg = 'Please import a valid water level data file first.'
            self.ConsoleSignal.emit('<font color=red>%s</font>' % msg)
            self.emit_warning(msg)
            return

        self.update_graph_layout_parameter()

        # Generate and Display Graph :

        for i in range(5):
            QCoreApplication.processEvents()

        QApplication.setOverrideCursor(Qt.WaitCursor)

        self.hydrograph.set_wldset(self.dmngr.get_current_wldset())
        self.hydrograph.set_wxdset(self.dmngr.get_current_wxdset())
        self.hydrograph.generate_hydrograph()

        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

        QApplication.restoreOverrideCursor()

    def select_save_path(self):
        """
        Open a dialog where the user can select a file name to save the
        hydrograph.
        """
        if self.wldset is None:
            return

        ffmat = "*.pdf;;*.svg;;*.png"
        fname = find_unique_filename(osp.join(
            self.save_fig_dir, 'hydrograph_%s.pdf' % self.wldset['Well']))

        fname, ftype = QFileDialog.getSaveFileName(
            self, "Save Figure", fname, ffmat)
        if fname:
            ftype = ftype.replace('*', '')
            fname = fname if fname.endswith(ftype) else fname + ftype
            self.save_fig_dir = os.path.dirname(fname)

            try:
                self.save_figure(fname)
            except PermissionError:
                msg = "The file is in use by another application or user."
                QMessageBox.warning(self, 'Warning', msg, QMessageBox.Ok)
                self.select_save_path()

    def save_figure(self, fname):
        """Save the hydrograph figure in a file."""
        self.hydrograph.generate_hydrograph()
        self.hydrograph.savefig(fname)

    # ---- Graph Layout Handlers

    def load_layout_isClicked(self):
        """Handle when the button to load the graph layout is clicked."""
        if self.wldset is None:
            self.emit_warning(
                "Please import a valid water level data file first.")
            return

        layout = self.wldset.get_layout()
        if layout is None:
            self.emit_warning(
                "No graph layout exists for well %s." % self.wldset['Well'])
        else:
            self.load_graph_layout(layout)

    def load_graph_layout(self, layout):
        """Load the graph layout into the GUI."""

        self.__updateUI = False

        # Scales :

        date = layout['TIMEmin']
        date = xldate_as_tuple(date, 0)
        self.date_start_widget.setDate(QDate(date[0], date[1], date[2]))

        date = layout['TIMEmax']
        date = xldate_as_tuple(date, 0)
        self.date_end_widget.setDate(QDate(date[0], date[1], date[2]))

        self.dateDispFreq_spinBox.setValue(layout['date_labels_pattern'])

        self.waterlvl_scale.setValue(layout['WLscale'])
        self.waterlvl_max.setValue(layout['WLmin'])
        self.NZGridWL_spinBox.setValue(layout['NZGrid'])
        self.Ptot_scale.setValue(layout['RAINscale'])

        x = ['mbgs', 'masl'].index(layout['WLdatum'])
        self.datum_widget.setCurrentIndex(x)

        self.qweather_bin.setCurrentIndex(layout['bwidth_indx'])
        self.time_scale_label.setCurrentIndex(
            self.time_scale_label.findText(layout['datemode']))

        # ---- Language and colors
        self.btn_language.set_language(layout['language'])
        self.color_palette_win.load_colors()

        # ---- Page Setup

        self.page_setup_win.pageSize = (layout['fwidth'], layout['fheight'])
        self.page_setup_win.va_ratio = layout['va_ratio']
        self.page_setup_win.isLegend = layout['legend_on']
        self.page_setup_win.isGraphTitle = layout['title_on']
        self.page_setup_win.isTrendLine = layout['trend_line']
        self.page_setup_win.is_meteo_on = layout['meteo_on']
        self.page_setup_win.is_glue_wl_on = layout['glue_wl_on']
        self.page_setup_win.is_mrc_wl_on = layout['mrc_wl_on']
        self.page_setup_win.figframe_lw = layout['figframe_lw']

        self.page_setup_win.legend_on.set_value(layout['legend_on'])
        self.page_setup_win.title_on.set_value(layout['title_on'])
        self.page_setup_win.wltrend_on.set_value(layout['trend_line'])
        self.page_setup_win.meteo_on.set_value(layout['meteo_on'])
        self.page_setup_win.glue_wl_on.set_value(layout['glue_wl_on'])
        self.page_setup_win.mrc_wl_on.set_value(layout['mrc_wl_on'])
        self.page_setup_win.fframe_lw_widg.setValue(layout['figframe_lw'])

        self.page_setup_win.fwidth.setValue(layout['fwidth'])
        self.page_setup_win.fheight.setValue(layout['fheight'])
        self.page_setup_win.va_ratio_spinBox.setValue(layout['va_ratio'])

        # Check if Weather Dataset :

        if layout['wxdset'] in self.dmngr.wxdsets:
            self.dmngr.set_current_wxdset(layout['wxdset'])
        else:
            if self.dmngr.set_closest_wxdset() is None:
                self.draw_hydrograph()
        self.__updateUI = True

    def save_layout_isClicked(self):
        wldset = self.wldset
        if wldset is None:
            self.emit_warning(
                "Please import a valid water level data file first.")
            return

        layout = wldset.get_layout()
        if layout is not None:
            msg = ('A graph layout already exists for well %s.Do you want to'
                   ' you want to replace it?') % wldset['Well']
            reply = QMessageBox.question(self, 'Save Graph Layout', msg,
                                         QMessageBox.Yes | QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.save_graph_layout()
            elif reply == QMessageBox.No:
                msg = "Graph layout not saved for well %s." % wldset['Well']
                self.ConsoleSignal.emit('<font color=black>%s' % msg)
        else:
            self.save_graph_layout()

    def save_graph_layout(self):
        """Save the graph layout in the project hdf5 file."""
        print("Saving the graph layout for well %s..." % self.wldset['Well'],
              end=" ")

        layout = {'WLmin': self.waterlvl_max.value(),
                  'WLscale': self.waterlvl_scale.value(),
                  'RAINscale': self.Ptot_scale.value(),
                  'fwidth': self.page_setup_win.pageSize[0],
                  'fheight': self.page_setup_win.pageSize[1],
                  'va_ratio': self.page_setup_win.va_ratio,
                  'NZGrid': self.NZGridWL_spinBox.value(),
                  'bwidth_indx': self.qweather_bin.currentIndex(),
                  'date_labels_pattern': self.dateDispFreq_spinBox.value(),
                  'datemode': self.time_scale_label.currentText()}
        layout['wxdset'] = None if self.wxdset is None else self.wxdset.name

        year = self.date_start_widget.date().year()
        month = self.date_start_widget.date().month()
        layout['TIMEmin'] = xldate_from_date_tuple((year, month, 1), 0)

        year = self.date_end_widget.date().year()
        month = self.date_end_widget.date().month()
        layout['TIMEmax'] = xldate_from_date_tuple((year, month, 1), 0)

        if self.datum_widget.currentIndex() == 0:
            layout['WLdatum'] = 'mbgs'
        else:
            layout['WLdatum'] = 'masl'

        # ---- Page Setup

        layout['title_on'] = bool(self.page_setup_win.isGraphTitle)
        layout['legend_on'] = bool(self.page_setup_win.isLegend)
        layout['language'] = self.btn_language.language
        layout['trend_line'] = bool(self.page_setup_win.isTrendLine)
        layout['meteo_on'] = bool(self.page_setup_win.is_meteo_on)
        layout['glue_wl_on'] = bool(self.page_setup_win.is_glue_wl_on)
        layout['mrc_wl_on'] = bool(self.page_setup_win.is_mrc_wl_on)
        layout['figframe_lw'] = self.page_setup_win.figframe_lw

        # Save the colors :

        cdb = ColorsReader()
        cdb.load_colors_db()
        layout['colors'] = cdb.RGB

        # Save the layout :

        self.wldset.save_layout(layout)
        msg = 'Layout saved successfully for well %s.' % self.wldset['Well']
        self.ConsoleSignal.emit('<font color=black>%s</font>' % msg)
        print("done")
Example #2
0
 def createEditor(self, parent, option, index):
     dateedit = QDateEdit(parent)
     #dateedit=QDateTimeEdit(parent)
     dateedit.setDateRange(self.minimum, self.maximum)
     dateedit.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
     dateedit.setDisplayFormat(self.format)
     dateedit.setCalendarPopup(True)
     return dateedit
Example #3
0
 def createEditor(self, parent, option, index):
     if (index.column() == ACTIONID and index.model().data(
             index, Qt.DisplayRole) == ACQUIRED):  # Acquired is read-only
         return
     if index.column() == DATE:
         editor = QDateEdit(parent)
         editor.setMaximumDate(QDate.currentDate())
         editor.setDisplayFormat("yyyy-MM-dd")
         if PYQT_VERSION_STR >= "4.1.0":
             editor.setCalendarPopup(True)
         editor.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
         return editor
     else:
         return QSqlRelationalDelegate.createEditor(self, parent, option,
                                                    index)
Example #4
0
class HydroprintGUI(myqt.DialogWindow):

    ConsoleSignal = QSignal(str)

    def __init__(self, datamanager, parent=None):
        super(HydroprintGUI, self).__init__(parent, maximize=True)

        self.__updateUI = True

        # Child widgets:

        self.dmngr = datamanager
        self.dmngr.wldsetChanged.connect(self.wldset_changed)
        self.dmngr.wxdsetChanged.connect(self.wxdset_changed)

        self.page_setup_win = PageSetupWin(self)
        self.page_setup_win.newPageSetupSent.connect(self.layout_changed)

        self.color_palette_win = ColorsSetupWin(self)
        self.color_palette_win.newColorSetupSent.connect(self.update_colors)

        # Memory path variable:

        self.save_fig_dir = self.workdir

        # Generate UI:

        self.__initUI__()

    def __initUI__(self):

        # ---- Toolbar

        self.btn_save = btn_save = QToolButtonNormal(icons.get_icon('save'))
        btn_save.setToolTip('Save the well hydrograph')

        # btn_draw is usefull for debugging purposes
        btn_draw = QToolButtonNormal(icons.get_icon('refresh'))
        btn_draw.setToolTip('Force a refresh of the well hydrograph')
        btn_draw.hide()

        self.btn_load_layout = QToolButtonNormal(
            icons.get_icon('load_graph_config'))
        self.btn_load_layout.setToolTip(
            "<p>Load graph layout for the current water level "
            " datafile if it exists</p>")
        self.btn_load_layout.clicked.connect(self.load_layout_isClicked)

        self.btn_save_layout = QToolButtonNormal(
            icons.get_icon('save_graph_config'))
        self.btn_save_layout.setToolTip('Save current graph layout')
        self.btn_save_layout.clicked.connect(self.save_layout_isClicked)

        btn_bestfit_waterlvl = QToolButtonNormal(icons.get_icon('fit_y'))
        btn_bestfit_waterlvl.setToolTip('Best fit the water level scale')

        btn_bestfit_time = QToolButtonNormal(icons.get_icon('fit_x'))
        btn_bestfit_time.setToolTip('Best fit the time scale')

        self.btn_page_setup = QToolButtonNormal(icons.get_icon('page_setup'))
        self.btn_page_setup.setToolTip('Show the page setup window')
        self.btn_page_setup.clicked.connect(self.page_setup_win.show)

        btn_color_pick = QToolButtonNormal(icons.get_icon('color_picker'))
        btn_color_pick.setToolTip('<p>Show a window to setup the color palette'
                                  ' used to draw the hydrograph</p.')
        btn_color_pick.clicked.connect(self.color_palette_win.show)

        self.btn_language = LangToolButton()
        self.btn_language.setToolTip(
            "Set the language of the text shown in the graph.")
        self.btn_language.sig_lang_changed.connect(self.layout_changed)
        self.btn_language.setIconSize(icons.get_iconsize('normal'))

        # ---- Zoom Panel

        btn_zoom_out = QToolButtonSmall(icons.get_icon('zoom_out'))
        btn_zoom_out.setToolTip('Zoom out (ctrl + mouse-wheel-down)')
        btn_zoom_out.clicked.connect(self.zoom_out)

        btn_zoom_in = QToolButtonSmall(icons.get_icon('zoom_in'))
        btn_zoom_in.setToolTip('Zoom in (ctrl + mouse-wheel-up)')
        btn_zoom_in.clicked.connect(self.zoom_in)

        self.zoom_disp = QSpinBox()
        self.zoom_disp.setAlignment(Qt.AlignCenter)
        self.zoom_disp.setButtonSymbols(QAbstractSpinBox.NoButtons)
        self.zoom_disp.setReadOnly(True)
        self.zoom_disp.setSuffix(' %')
        self.zoom_disp.setRange(0, 9999)
        self.zoom_disp.setValue(100)

        zoom_pan = myqt.QFrameLayout()
        zoom_pan.setSpacing(3)
        zoom_pan.addWidget(btn_zoom_out, 0, 0)
        zoom_pan.addWidget(btn_zoom_in, 0, 1)
        zoom_pan.addWidget(self.zoom_disp, 0, 2)

        # LAYOUT :

        btn_list = [
            btn_save, btn_draw, self.btn_load_layout, self.btn_save_layout,
            VSep(), btn_bestfit_waterlvl, btn_bestfit_time,
            VSep(), self.btn_page_setup, btn_color_pick, self.btn_language,
            VSep(), zoom_pan
        ]

        subgrid_toolbar = QGridLayout()
        toolbar_widget = QWidget()

        row = 0
        for col, btn in enumerate(btn_list):
            subgrid_toolbar.addWidget(btn, row, col)

        subgrid_toolbar.setSpacing(5)
        subgrid_toolbar.setContentsMargins(0, 0, 0, 0)
        subgrid_toolbar.setColumnStretch(col + 1, 100)

        toolbar_widget.setLayout(subgrid_toolbar)

        # ---- LEFT PANEL

        # SubGrid Hydrograph Frame :

        self.hydrograph = hydrograph.Hydrograph()
        self.hydrograph_scrollarea = mplFigViewer.ImageViewer()
        self.hydrograph_scrollarea.zoomChanged.connect(self.zoom_disp.setValue)

        grid_hydrograph = QGridLayout()
        grid_hydrograph.addWidget(self.hydrograph_scrollarea, 0, 0)
        grid_hydrograph.setRowStretch(0, 500)
        grid_hydrograph.setColumnStretch(0, 500)
        grid_hydrograph.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)

        # ASSEMBLING SubGrids :

        grid_layout = QGridLayout()
        self.grid_layout_widget = QFrame()

        row = 0
        grid_layout.addWidget(toolbar_widget, row, 0)
        row += 1
        grid_layout.addLayout(grid_hydrograph, row, 0)

        grid_layout.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)
        grid_layout.setSpacing(5)
        grid_layout.setColumnStretch(0, 500)
        grid_layout.setRowStretch(1, 500)

        self.grid_layout_widget.setLayout(grid_layout)

        # ---- Right Panel

        self.tabscales = self.__init_scalesTabWidget__()

        self.right_panel = myqt.QFrameLayout()
        self.right_panel.addWidget(self.dmngr, 0, 0)
        self.right_panel.addWidget(self.tabscales, 1, 0)
        self.right_panel.setRowStretch(2, 100)

        self.right_panel.setSpacing(15)

        # ---- MAIN GRID

        mainGrid = QGridLayout()

        mainGrid.addWidget(self.grid_layout_widget, 0, 0)
        mainGrid.addWidget(VSep(), 0, 1)
        mainGrid.addWidget(self.right_panel, 0, 2)

        mainGrid.setContentsMargins(10, 10, 10, 10)  # (L, T, R, B)
        mainGrid.setSpacing(15)
        mainGrid.setColumnStretch(0, 500)
        mainGrid.setColumnMinimumWidth(2, 250)

        self.setLayout(mainGrid)

        # ---- EVENTS

        # Toolbox Layout :

        btn_bestfit_waterlvl.clicked.connect(self.best_fit_waterlvl)
        btn_bestfit_time.clicked.connect(self.best_fit_time)
        btn_draw.clicked.connect(self.draw_hydrograph)
        btn_save.clicked.connect(self.select_save_path)

        # Hydrograph Layout :

        self.Ptot_scale.valueChanged.connect(self.layout_changed)
        self.qweather_bin.currentIndexChanged.connect(self.layout_changed)

        # ---- Init Image

        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def __init_scalesTabWidget__(self):
        class QRowLayout(QGridLayout):
            def __init__(self, items, parent=None):
                super(QRowLayout, self).__init__(parent)

                for col, item in enumerate(items):
                    self.addWidget(item, 0, col)

                self.setContentsMargins(0, 0, 0, 0)
                self.setColumnStretch(0, 100)

        # ---- Time axis properties

        # Generate the widgets :

        self.date_start_widget = QDateEdit()
        self.date_start_widget.setDisplayFormat('01 / MM / yyyy')
        self.date_start_widget.setAlignment(Qt.AlignCenter)
        self.date_start_widget.dateChanged.connect(self.layout_changed)

        self.date_end_widget = QDateEdit()
        self.date_end_widget.setDisplayFormat('01 / MM / yyyy')
        self.date_end_widget.setAlignment(Qt.AlignCenter)
        self.date_end_widget.dateChanged.connect(self.layout_changed)

        self.time_scale_label = QComboBox()
        self.time_scale_label.setEditable(False)
        self.time_scale_label.setInsertPolicy(QComboBox.NoInsert)
        self.time_scale_label.addItems(['Month', 'Year'])
        self.time_scale_label.setCurrentIndex(0)
        self.time_scale_label.currentIndexChanged.connect(self.layout_changed)

        self.dateDispFreq_spinBox = QSpinBox()
        self.dateDispFreq_spinBox.setSingleStep(1)
        self.dateDispFreq_spinBox.setMinimum(1)
        self.dateDispFreq_spinBox.setMaximum(100)
        self.dateDispFreq_spinBox.setValue(self.hydrograph.date_labels_pattern)
        self.dateDispFreq_spinBox.setAlignment(Qt.AlignCenter)
        self.dateDispFreq_spinBox.setKeyboardTracking(False)
        self.dateDispFreq_spinBox.valueChanged.connect(self.layout_changed)

        # Setting up the layout :

        widget_time_scale = QFrame()
        widget_time_scale.setFrameStyle(0)
        grid_time_scale = QGridLayout()

        GRID = [[QLabel('From :'), self.date_start_widget],
                [QLabel('To :'), self.date_end_widget],
                [QLabel('Scale :'), self.time_scale_label],
                [QLabel('Date Disp. Pattern:'), self.dateDispFreq_spinBox]]

        for i, ROW in enumerate(GRID):
            grid_time_scale.addLayout(QRowLayout(ROW), i, 1)

        grid_time_scale.setVerticalSpacing(5)
        grid_time_scale.setContentsMargins(10, 10, 10, 10)

        widget_time_scale.setLayout(grid_time_scale)

        # ----- Water level axis properties

        # Widget :

        self.waterlvl_scale = QDoubleSpinBox()
        self.waterlvl_scale.setSingleStep(0.05)
        self.waterlvl_scale.setMinimum(0.05)
        self.waterlvl_scale.setSuffix('  m')
        self.waterlvl_scale.setAlignment(Qt.AlignCenter)
        self.waterlvl_scale.setKeyboardTracking(False)
        self.waterlvl_scale.valueChanged.connect(self.layout_changed)
        self.waterlvl_scale.setFixedWidth(100)

        self.waterlvl_max = QDoubleSpinBox()
        self.waterlvl_max.setSingleStep(0.1)
        self.waterlvl_max.setSuffix('  m')
        self.waterlvl_max.setAlignment(Qt.AlignCenter)
        self.waterlvl_max.setMinimum(-1000)
        self.waterlvl_max.setMaximum(1000)
        self.waterlvl_max.setKeyboardTracking(False)
        self.waterlvl_max.valueChanged.connect(self.layout_changed)
        self.waterlvl_max.setFixedWidth(100)

        self.NZGridWL_spinBox = QSpinBox()
        self.NZGridWL_spinBox.setSingleStep(1)
        self.NZGridWL_spinBox.setMinimum(1)
        self.NZGridWL_spinBox.setMaximum(50)
        self.NZGridWL_spinBox.setValue(self.hydrograph.NZGrid)
        self.NZGridWL_spinBox.setAlignment(Qt.AlignCenter)
        self.NZGridWL_spinBox.setKeyboardTracking(False)
        self.NZGridWL_spinBox.valueChanged.connect(self.layout_changed)
        self.NZGridWL_spinBox.setFixedWidth(100)

        self.datum_widget = QComboBox()
        self.datum_widget.addItems(['Ground Surface', 'Sea Level'])
        self.datum_widget.currentIndexChanged.connect(self.layout_changed)

        # Layout :

        subgrid_WLScale = QGridLayout()

        GRID = [[QLabel('Minimum :'), self.waterlvl_max],
                [QLabel('Scale :'), self.waterlvl_scale],
                [QLabel('Grid Divisions :'), self.NZGridWL_spinBox],
                [QLabel('Datum :'), self.datum_widget]]

        for i, ROW in enumerate(GRID):
            subgrid_WLScale.addLayout(QRowLayout(ROW), i, 1)

        subgrid_WLScale.setVerticalSpacing(5)
        subgrid_WLScale.setContentsMargins(10, 10, 10, 10)  # (L, T, R, B)

        WLScale_widget = QFrame()
        WLScale_widget.setFrameStyle(0)
        WLScale_widget.setLayout(subgrid_WLScale)

        # ---- Weather Axis

        # Widgets :

        self.Ptot_scale = QSpinBox()
        self.Ptot_scale.setSingleStep(5)
        self.Ptot_scale.setMinimum(5)
        self.Ptot_scale.setMaximum(500)
        self.Ptot_scale.setValue(20)
        self.Ptot_scale.setSuffix('  mm')
        self.Ptot_scale.setAlignment(Qt.AlignCenter)

        self.qweather_bin = QComboBox()
        self.qweather_bin.setEditable(False)
        self.qweather_bin.setInsertPolicy(QComboBox.NoInsert)
        self.qweather_bin.addItems(['day', 'week', 'month'])
        self.qweather_bin.setCurrentIndex(1)

        # Layout :

        layout = QGridLayout()

        GRID = [[QLabel('Precip. Scale :'), self.Ptot_scale],
                [QLabel('Resampling :'), self.qweather_bin]]

        for i, row in enumerate(GRID):
            layout.addLayout(QRowLayout(row), i, 1)

        layout.setVerticalSpacing(5)
        layout.setContentsMargins(10, 10, 10, 10)  # (L,T,R,B)
        layout.setRowStretch(i + 1, 100)

        widget_weather_scale = QFrame()
        widget_weather_scale.setFrameStyle(0)
        widget_weather_scale.setLayout(layout)

        # ---- ASSEMBLING TABS

        tabscales = QTabWidget()
        tabscales.addTab(widget_time_scale, 'Time')
        tabscales.addTab(WLScale_widget, 'Water Level')
        tabscales.addTab(widget_weather_scale, 'Weather')

        return tabscales

    @property
    def workdir(self):
        return self.dmngr.workdir

    # ---- Utilities

    def zoom_in(self):
        self.hydrograph_scrollarea.zoomIn()

    def zoom_out(self):
        self.hydrograph_scrollarea.zoomOut()

    def update_colors(self):
        self.hydrograph.update_colors()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    # ---- Datasets Handlers

    @property
    def wldset(self):
        return self.dmngr.get_current_wldset()

    @property
    def wxdset(self):
        return self.dmngr.get_current_wxdset()

    def wldset_changed(self):
        """Handle when the water level dataset of the datamanager changes."""
        if self.wldset is None:
            self.clear_hydrograph()
            return
        else:
            wldset = self.wldset
            self.hydrograph.set_wldset(wldset)
            self.hydrograph.gluedf = self.wldset.get_glue_at(-1)

        # Load the manual measurements.

        fname = os.path.join(self.workdir, "Water Levels",
                             'waterlvl_manual_measurements')
        tmeas, wlmeas = load_waterlvl_measures(fname, wldset['Well'])
        wldset.set_wlmeas(tmeas, wlmeas)

        # Setup the layout of the hydrograph.

        layout = wldset.get_layout()
        if layout is not None:
            msg = 'Loading existing graph layout for well %s.' % wldset['Well']
            print(msg)
            self.ConsoleSignal.emit('<font color=black>%s</font>' % msg)
            self.load_graph_layout(layout)
        else:
            print('No graph layout exists for well %s.' % wldset['Well'])
            # Fit Water Level in Layout :
            self.__updateUI = False
            self.best_fit_waterlvl()
            self.best_fit_time()
            self.dmngr.set_closest_wxdset()
            self.__updateUI = True

    def wxdset_changed(self):
        """Handle when the weather dataset of the datamanager changes."""
        if self.wldset is None:
            self.clear_hydrograph()
        else:
            self.hydrograph.set_wxdset(self.wxdset)
            QCoreApplication.processEvents()
            self.draw_hydrograph()

    # ---- Draw Hydrograph Handlers

    def best_fit_waterlvl(self):
        wldset = self.dmngr.get_current_wldset()
        if wldset is not None:
            WLscale, WLmin = self.hydrograph.best_fit_waterlvl()
            self.waterlvl_scale.setValue(WLscale)
            self.waterlvl_max.setValue(WLmin)

    def best_fit_time(self):
        wldset = self.dmngr.get_current_wldset()
        if wldset is not None:
            date0, date1 = self.hydrograph.best_fit_time(wldset['Time'])
            self.date_start_widget.setDate(QDate(date0[0], date0[1], date0[2]))
            self.date_end_widget.setDate(QDate(date1[0], date1[1], date1[2]))

    @QSlot()
    def mrc_wl_changed(self):
        """
        Force a redraw of the MRC water levels after the results have
        changed for the dataset.
        """
        self.hydrograph.draw_mrc_wl()
        self.hydrograph.setup_legend()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    @QSlot(GLUEDataFrameBase)
    def glue_wl_changed(self, gluedf):
        """
        Force a redraw of the GLUE water levels after the results have
        changed for the dataset.
        """
        self.hydrograph.set_gluedf(gluedf)
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def layout_changed(self):
        """
        When an element of the graph layout is changed in the UI.
        """

        if self.__updateUI is False:
            return

        self.update_graph_layout_parameter()

        if self.hydrograph.isHydrographExists is False:
            return

        sender = self.sender()

        if sender == self.btn_language:
            self.hydrograph.draw_ylabels()
            self.hydrograph.setup_xticklabels()
            self.hydrograph.setup_legend()
        elif sender in [self.waterlvl_max, self.waterlvl_scale]:
            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.NZGridWL_spinBox:
            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.update_precip_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.Ptot_scale:
            self.hydrograph.update_precip_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.datum_widget:
            yoffset = int(self.wldset['Elevation'] / self.hydrograph.WLscale)
            yoffset *= self.hydrograph.WLscale

            self.hydrograph.WLmin = (yoffset - self.hydrograph.WLmin)

            self.waterlvl_max.blockSignals(True)
            self.waterlvl_max.setValue(self.hydrograph.WLmin)
            self.waterlvl_max.blockSignals(False)

            # This is calculated so that trailing zeros in the altitude of the
            # well is not carried to the y axis labels, so that they remain a
            # int multiple of *WLscale*.

            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.draw_waterlvl()
            self.hydrograph.draw_ylabels()
        elif sender in [self.date_start_widget, self.date_end_widget]:
            self.hydrograph.set_time_scale()
            self.hydrograph.draw_weather()
            self.hydrograph.draw_figure_title()
        elif sender == self.dateDispFreq_spinBox:
            self.hydrograph.set_time_scale()
            self.hydrograph.setup_xticklabels()
        elif sender == self.page_setup_win:
            self.hydrograph.update_fig_size()
            # Implicitly call : set_margins()
            #                   draw_ylabels()
            #                   set_time_scale()
            #                   draw_figure_title
            self.hydrograph.draw_waterlvl()
            self.hydrograph.setup_legend()
        elif sender == self.qweather_bin:
            self.hydrograph.resample_bin()
            self.hydrograph.draw_weather()
            self.hydrograph.draw_ylabels()
        elif sender == self.time_scale_label:
            self.hydrograph.set_time_scale()
            self.hydrograph.draw_weather()
        else:
            print('No action for this widget yet.')

        # !!!! temporary fix until I can find a better solution !!!!

#        sender.blockSignals(True)
        if type(sender) in [QDoubleSpinBox, QSpinBox]:
            sender.setReadOnly(True)

        for i in range(10):
            QCoreApplication.processEvents()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)
        for i in range(10):
            QCoreApplication.processEvents()

        if type(sender) in [QDoubleSpinBox, QSpinBox]:
            sender.setReadOnly(False)


#        sender.blockSignals(False)

    def update_graph_layout_parameter(self):

        # language :

        self.hydrograph.language = self.btn_language.language

        # Scales :

        self.hydrograph.WLmin = self.waterlvl_max.value()
        self.hydrograph.WLscale = self.waterlvl_scale.value()
        self.hydrograph.RAINscale = self.Ptot_scale.value()
        self.hydrograph.NZGrid = self.NZGridWL_spinBox.value()

        # WL Datum :

        self.hydrograph.WLdatum = self.datum_widget.currentIndex()

        # Dates :

        self.hydrograph.datemode = self.time_scale_label.currentText()

        year = self.date_start_widget.date().year()
        month = self.date_start_widget.date().month()
        self.hydrograph.TIMEmin = xldate_from_date_tuple((year, month, 1), 0)

        year = self.date_end_widget.date().year()
        month = self.date_end_widget.date().month()
        self.hydrograph.TIMEmax = xldate_from_date_tuple((year, month, 1), 0)

        self.hydrograph.date_labels_pattern = self.dateDispFreq_spinBox.value()

        # Page Setup :

        self.hydrograph.fwidth = self.page_setup_win.pageSize[0]
        self.hydrograph.fheight = self.page_setup_win.pageSize[1]
        self.hydrograph.va_ratio = self.page_setup_win.va_ratio

        self.hydrograph.trend_line = self.page_setup_win.isTrendLine
        self.hydrograph.isLegend = self.page_setup_win.isLegend
        self.hydrograph.isGraphTitle = self.page_setup_win.isGraphTitle
        self.hydrograph.set_meteo_on(self.page_setup_win.is_meteo_on)
        self.hydrograph.set_glue_wl_on(self.page_setup_win.is_glue_wl_on)
        self.hydrograph.set_mrc_wl_on(self.page_setup_win.is_mrc_wl_on)
        self.hydrograph.set_figframe_lw(self.page_setup_win.figframe_lw)

        # Weather bins :

        self.hydrograph.bwidth_indx = self.qweather_bin.currentIndex()

    def clear_hydrograph(self):
        """Clear the hydrograph figure to show only a blank canvas."""
        self.hydrograph.clf()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def draw_hydrograph(self):
        if self.dmngr.wldataset_count() == 0:
            msg = 'Please import a valid water level data file first.'
            self.ConsoleSignal.emit('<font color=red>%s</font>' % msg)
            self.emit_warning(msg)
            return

        self.update_graph_layout_parameter()

        # Generate and Display Graph :

        for i in range(5):
            QCoreApplication.processEvents()

        QApplication.setOverrideCursor(Qt.WaitCursor)

        self.hydrograph.set_wldset(self.dmngr.get_current_wldset())
        self.hydrograph.set_wxdset(self.dmngr.get_current_wxdset())
        self.hydrograph.generate_hydrograph()

        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

        QApplication.restoreOverrideCursor()

    def select_save_path(self):
        """
        Open a dialog where the user can select a file name to save the
        hydrograph.
        """
        if self.wldset is None:
            return

        ffmat = "*.pdf;;*.svg;;*.png"
        fname = find_unique_filename(
            osp.join(self.save_fig_dir,
                     'hydrograph_%s.pdf' % self.wldset['Well']))

        fname, ftype = QFileDialog.getSaveFileName(self, "Save Figure", fname,
                                                   ffmat)
        if fname:
            ftype = ftype.replace('*', '')
            fname = fname if fname.endswith(ftype) else fname + ftype
            self.save_fig_dir = os.path.dirname(fname)

            try:
                self.save_figure(fname)
            except PermissionError:
                msg = "The file is in use by another application or user."
                QMessageBox.warning(self, 'Warning', msg, QMessageBox.Ok)
                self.select_save_path()

    def save_figure(self, fname):
        """Save the hydrograph figure in a file."""
        self.hydrograph.generate_hydrograph()
        self.hydrograph.savefig(fname)

    # ---- Graph Layout Handlers

    def load_layout_isClicked(self):
        """Handle when the button to load the graph layout is clicked."""
        if self.wldset is None:
            self.emit_warning(
                "Please import a valid water level data file first.")
            return

        layout = self.wldset.get_layout()
        if layout is None:
            self.emit_warning("No graph layout exists for well %s." %
                              self.wldset['Well'])
        else:
            self.load_graph_layout(layout)

    def load_graph_layout(self, layout):
        """Load the graph layout into the GUI."""

        self.__updateUI = False

        # Scales :

        date = layout['TIMEmin']
        date = xldate_as_tuple(date, 0)
        self.date_start_widget.setDate(QDate(date[0], date[1], date[2]))

        date = layout['TIMEmax']
        date = xldate_as_tuple(date, 0)
        self.date_end_widget.setDate(QDate(date[0], date[1], date[2]))

        self.dateDispFreq_spinBox.setValue(layout['date_labels_pattern'])

        self.waterlvl_scale.setValue(layout['WLscale'])
        self.waterlvl_max.setValue(layout['WLmin'])
        self.NZGridWL_spinBox.setValue(layout['NZGrid'])
        self.Ptot_scale.setValue(layout['RAINscale'])

        x = ['mbgs', 'masl'].index(layout['WLdatum'])
        self.datum_widget.setCurrentIndex(x)

        self.qweather_bin.setCurrentIndex(layout['bwidth_indx'])
        self.time_scale_label.setCurrentIndex(
            self.time_scale_label.findText(layout['datemode']))

        # ---- Language and colors
        self.btn_language.set_language(layout['language'])
        self.color_palette_win.load_colors()

        # ---- Page Setup

        self.page_setup_win.pageSize = (layout['fwidth'], layout['fheight'])
        self.page_setup_win.va_ratio = layout['va_ratio']
        self.page_setup_win.isLegend = layout['legend_on']
        self.page_setup_win.isGraphTitle = layout['title_on']
        self.page_setup_win.isTrendLine = layout['trend_line']
        self.page_setup_win.is_meteo_on = layout['meteo_on']
        self.page_setup_win.is_glue_wl_on = layout['glue_wl_on']
        self.page_setup_win.is_mrc_wl_on = layout['mrc_wl_on']
        self.page_setup_win.figframe_lw = layout['figframe_lw']

        self.page_setup_win.legend_on.set_value(layout['legend_on'])
        self.page_setup_win.title_on.set_value(layout['title_on'])
        self.page_setup_win.wltrend_on.set_value(layout['trend_line'])
        self.page_setup_win.meteo_on.set_value(layout['meteo_on'])
        self.page_setup_win.glue_wl_on.set_value(layout['glue_wl_on'])
        self.page_setup_win.mrc_wl_on.set_value(layout['mrc_wl_on'])
        self.page_setup_win.fframe_lw_widg.setValue(layout['figframe_lw'])

        self.page_setup_win.fwidth.setValue(layout['fwidth'])
        self.page_setup_win.fheight.setValue(layout['fheight'])
        self.page_setup_win.va_ratio_spinBox.setValue(layout['va_ratio'])

        # Check if Weather Dataset :

        if layout['wxdset'] in self.dmngr.wxdsets:
            self.dmngr.set_current_wxdset(layout['wxdset'])
        else:
            self.dmngr.set_closest_wxdset()

        self.__updateUI = True

    def save_layout_isClicked(self):
        wldset = self.wldset
        if wldset is None:
            self.emit_warning(
                "Please import a valid water level data file first.")
            return

        layout = wldset.get_layout()
        if layout is not None:
            msg = ('A graph layout already exists for well %s.Do you want to'
                   ' you want to replace it?') % wldset['Well']
            reply = QMessageBox.question(self, 'Save Graph Layout', msg,
                                         QMessageBox.Yes | QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.save_graph_layout()
            elif reply == QMessageBox.No:
                msg = "Graph layout not saved for well %s." % wldset['Well']
                self.ConsoleSignal.emit('<font color=black>%s' % msg)
        else:
            self.save_graph_layout()

    def save_graph_layout(self):
        """Save the graph layout in the project hdf5 file."""
        print("Saving the graph layout for well %s..." % self.wldset['Well'],
              end=" ")

        layout = {
            'WLmin': self.waterlvl_max.value(),
            'WLscale': self.waterlvl_scale.value(),
            'RAINscale': self.Ptot_scale.value(),
            'fwidth': self.page_setup_win.pageSize[0],
            'fheight': self.page_setup_win.pageSize[1],
            'va_ratio': self.page_setup_win.va_ratio,
            'NZGrid': self.NZGridWL_spinBox.value(),
            'bwidth_indx': self.qweather_bin.currentIndex(),
            'date_labels_pattern': self.dateDispFreq_spinBox.value(),
            'datemode': self.time_scale_label.currentText()
        }
        layout['wxdset'] = None if self.wxdset is None else self.wxdset.name

        year = self.date_start_widget.date().year()
        month = self.date_start_widget.date().month()
        layout['TIMEmin'] = xldate_from_date_tuple((year, month, 1), 0)

        year = self.date_end_widget.date().year()
        month = self.date_end_widget.date().month()
        layout['TIMEmax'] = xldate_from_date_tuple((year, month, 1), 0)

        if self.datum_widget.currentIndex() == 0:
            layout['WLdatum'] = 'mbgs'
        else:
            layout['WLdatum'] = 'masl'

        # ---- Page Setup

        layout['title_on'] = bool(self.page_setup_win.isGraphTitle)
        layout['legend_on'] = bool(self.page_setup_win.isLegend)
        layout['language'] = self.btn_language.language
        layout['trend_line'] = bool(self.page_setup_win.isTrendLine)
        layout['meteo_on'] = bool(self.page_setup_win.is_meteo_on)
        layout['glue_wl_on'] = bool(self.page_setup_win.is_glue_wl_on)
        layout['mrc_wl_on'] = bool(self.page_setup_win.is_mrc_wl_on)
        layout['figframe_lw'] = self.page_setup_win.figframe_lw

        # Save the colors :

        cdb = ColorsReader()
        cdb.load_colors_db()
        layout['colors'] = cdb.RGB

        # Save the layout :

        self.wldset.save_layout(layout)
        msg = 'Layout saved successfully for well %s.' % self.wldset['Well']
        self.ConsoleSignal.emit('<font color=black>%s</font>' % msg)
        print("done")
class TransactionWindow(BaseWindow):
    def __init__(self, mainWindows=None):
        super().__init__(mainWindows, "Transações", 0.25, 0, 0.5, 0.55,
                         "budget.png")
        self.createLayout()

    def addTransaction(self):
        dialog = AddTransDialog(self, "Adicionar Transação: ")
        result = dialog.exec_()
        if result:
            dialog.process(None)

    def addTransfer(self):
        dialog = AddTransfer(self)
        result = dialog.exec_()
        if result:
            dialog.process()
            self.mainWindow.loadInfo()

    def createLayout(self):
        base = QWidget()
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)

        monthBox = QHBoxLayout()
        self.date = QDateEdit(QDate.currentDate())
        self.date.setCalendarPopup(True)
        self.date.setDisplayFormat("MMMM yyyy")
        self.date.setAlignment(Qt.AlignCenter)

        def dateChanged():
            msg = "Exibindo transações de %s" % (
                self.date.date().toString("MM-yyyy"))
            self.mainWindow.showMessage(msg)
            self.mainWindow.loadInfo()

        self.date.dateChanged.connect(dateChanged)

        left = QToolButton()
        left.setArrowType(Qt.LeftArrow)
        left.clicked.connect(
            lambda: self.date.setDate(self.date.date().addMonths(-1)))

        right = QToolButton()
        right.setArrowType(Qt.RightArrow)
        right.clicked.connect(
            lambda: self.date.setDate(self.date.date().addMonths(1)))

        monthBox.addStretch(1)
        monthBox.addWidget(left)
        monthBox.addWidget(self.date, 1)
        monthBox.addWidget(right)
        monthBox.addStretch(1)

        self.view = Tree(6)
        self.view.setResizeColumn(2)
        self.view.setColumnWidth(0, 75)
        self.view.setColumnWidth(1, 175)
        self.view.setColumnWidth(2, 200)
        self.view.setColumnWidth(3, 100)
        self.view.setColumnWidth(5, 10)
        self.view.itemDoubleClicked.connect(self.editItem)

        layout.addLayout(monthBox)
        layout.addWidget(self.view, 1)
        base.setLayout(layout)
        self.setWidget(base)
        self.setFocusPolicy(Qt.StrongFocus)

    def createMenusAndToolbar(self):
        add = self.mainWindow.operationsMenu.addAction("Adicionar Transação")
        add.setIcon(Icon("money.png"))
        add.triggered.connect(self.addTransaction)

        addTransfer = self.mainWindow.operationsMenu.addAction(
            "Adicionar Transferência")
        addTransfer.setIcon(Icon("transfer.png"))
        addTransfer.triggered.connect(self.addTransfer)

        delete = self.mainWindow.operationsMenu.addAction("Apagar Transações")
        delete.setIcon(Icon("del-trans.png"))
        delete.triggered.connect(self.deleteItems)

        toolbar = QToolBar("Operações de Transação")
        toolbar.addAction(add)
        toolbar.addAction(addTransfer)
        toolbar.addAction(delete)
        self.mainWindow.addToolBar(toolbar)

    def deleteItem(self, item):
        date = item.parent().data(0, Qt.DisplayRole)
        result = QMessageBox.question(
            self, "Apagar Transação",
            "Deseja realmente apagar a transação:\n\n" +
            ">%s\n" % date.toString("dd/MM/yyyy") + ">%s\n" % item.text(1) +
            ">Conta:\t\t%s\n" % item.text(3) +
            ">Categoria:\t%s\n" % item.text(2) +
            ">Valor:\t\t%s" % item.text(4))
        if result == QMessageBox.Yes:
            DATABASE.delTransaction(item.ident)
            self.mainWindow.showMessage("Transação Apagada.")
            self.mainWindow.loadInfo()

    def deleteItems(self):
        items = self.view.getChecked(0)
        if items:
            result = QMessageBox.question(
                self, "Apagar Transações",
                "Deseja realmente apagar as %d transações selecionadas?" %
                len(items))
            if result == QMessageBox.Yes:
                for item in items:
                    DATABASE.delTransaction(item.ident)
                self.mainWindow.showMessage("%d transações Apagadas." %
                                            len(items))
                self.mainWindow.loadInfo()

    def editItem(self, item, column, invoice=False):
        if item.parent() or invoice:
            dialog = AddTransDialog(self, "Editar Transação: ", item)
            result = dialog.exec_()
            if result:
                dialog.process(item)

    def loadInfo(self):
        self.view.clear()
        self.view.setHeaderLabels(COLUMNS)
        for item in DATABASE.getTransactions(self.date.date()):
            itemDate = QDate(item[3], item[2], item[1])
            topDate = self.view.getTopItem(itemDate)
            if topDate:
                reg = TreeItem(topDate, '')
            else:
                top = TreeItem(self.view, '')
                top.setData(0, Qt.DisplayRole, itemDate)
                reg = TreeItem(top, '')
            reg.setIdent(item[0])
            reg.setCheckState(0, Qt.Unchecked)
            reg.setText(1, item[4])
            reg.setText(2, item[5])
            reg.setTextAlignment(2, Qt.AlignCenter)
            reg.setText(3, item[7])
            reg.setTextAlignment(3, Qt.AlignCenter)
            reg.setText(4, "R$ %.2f  " % item[8])
            reg.setTextAlignment(4, Qt.AlignRight)
            delBtn = DeleteButton(treeItem=reg)
            delBtn.clicked.connect(self.deleteItem)
            self.view.setItemWidget(reg, 5, delBtn)

            self.view.resizeColumnToContents(0)
            self.view.expandAll()
            self.view.sortItems(0, Qt.DescendingOrder)
Example #6
0
class PaymentDlg(QDialog):

    def __init__(self, parent=None):
        super(PaymentDlg, self).__init__(parent)

        forenameLabel = QLabel("&Forename:")
        self.forenameLineEdit = QLineEdit()
        forenameLabel.setBuddy(self.forenameLineEdit)
        surnameLabel = QLabel("&Surname:")
        self.surnameLineEdit = QLineEdit()
        surnameLabel.setBuddy(self.surnameLineEdit)
        invoiceLabel = QLabel("&Invoice No.:")
        self.invoiceSpinBox = QSpinBox()
        invoiceLabel.setBuddy(self.invoiceSpinBox)
        self.invoiceSpinBox.setRange(1, 10000000)
        self.invoiceSpinBox.setValue(100000)
        self.invoiceSpinBox.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
        amountLabel = QLabel("&Amount:")
        self.amountSpinBox = QDoubleSpinBox()
        amountLabel.setBuddy(self.amountSpinBox)
        self.amountSpinBox.setRange(0, 5000.0)
        self.amountSpinBox.setPrefix("$ ")
        self.amountSpinBox.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
        self.paidCheckBox = QCheckBox("&Paid")
        checkNumLabel = QLabel("Check &No.:")
        self.checkNumLineEdit = QLineEdit()
        checkNumLabel.setBuddy(self.checkNumLineEdit)
        bankLabel = QLabel("&Bank:")
        self.bankLineEdit = QLineEdit()
        bankLabel.setBuddy(self.bankLineEdit)
        accountNumLabel = QLabel("Acco&unt No.:")
        self.accountNumLineEdit = QLineEdit()
        accountNumLabel.setBuddy(self.accountNumLineEdit)
        sortCodeLabel = QLabel("Sort &Code:")
        self.sortCodeLineEdit = QLineEdit()
        sortCodeLabel.setBuddy(self.sortCodeLineEdit)
        creditCardLabel = QLabel("&Number:")
        self.creditCardLineEdit = QLineEdit()
        creditCardLabel.setBuddy(self.creditCardLineEdit)
        validFromLabel = QLabel("&Valid From:")
        self.validFromDateEdit = QDateEdit()
        validFromLabel.setBuddy(self.validFromDateEdit)
        self.validFromDateEdit.setDisplayFormat("MMM yyyy")
        self.validFromDateEdit.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
        expiryLabel = QLabel("E&xpiry Date:")
        self.expiryDateEdit = QDateEdit()
        expiryLabel.setBuddy(self.expiryDateEdit)
        self.expiryDateEdit.setDisplayFormat("MMM yyyy")
        self.expiryDateEdit.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok|
                                          QDialogButtonBox.Cancel)

        tabWidget = QTabWidget()
        cashWidget = QWidget()
        cashLayout = QHBoxLayout()
        cashLayout.addWidget(self.paidCheckBox)
        cashWidget.setLayout(cashLayout)
        tabWidget.addTab(cashWidget, "Cas&h")
        checkWidget = QWidget()
        checkLayout = QGridLayout()
        checkLayout.addWidget(checkNumLabel, 0, 0)
        checkLayout.addWidget(self.checkNumLineEdit, 0, 1)
        checkLayout.addWidget(bankLabel, 0, 2)
        checkLayout.addWidget(self.bankLineEdit, 0, 3)
        checkLayout.addWidget(accountNumLabel, 1, 0)
        checkLayout.addWidget(self.accountNumLineEdit, 1, 1)
        checkLayout.addWidget(sortCodeLabel, 1, 2)
        checkLayout.addWidget(self.sortCodeLineEdit, 1, 3)
        checkWidget.setLayout(checkLayout)
        tabWidget.addTab(checkWidget, "Chec&k")
        creditWidget = QWidget()
        creditLayout = QGridLayout()
        creditLayout.addWidget(creditCardLabel, 0, 0)
        creditLayout.addWidget(self.creditCardLineEdit, 0, 1, 1, 3)
        creditLayout.addWidget(validFromLabel, 1, 0)
        creditLayout.addWidget(self.validFromDateEdit, 1, 1)
        creditLayout.addWidget(expiryLabel, 1, 2)
        creditLayout.addWidget(self.expiryDateEdit, 1, 3)
        creditWidget.setLayout(creditLayout)
        tabWidget.addTab(creditWidget, "Credit Car&d")

        gridLayout = QGridLayout()
        gridLayout.addWidget(forenameLabel, 0, 0)
        gridLayout.addWidget(self.forenameLineEdit, 0, 1)
        gridLayout.addWidget(surnameLabel, 0, 2)
        gridLayout.addWidget(self.surnameLineEdit, 0, 3)
        gridLayout.addWidget(invoiceLabel, 1, 0)
        gridLayout.addWidget(self.invoiceSpinBox, 1, 1)
        gridLayout.addWidget(amountLabel, 1, 2)
        gridLayout.addWidget(self.amountSpinBox, 1, 3)
        layout = QVBoxLayout()
        layout.addLayout(gridLayout)
        layout.addWidget(tabWidget)
        layout.addWidget(self.buttonBox)
        self.setLayout(layout)

        for lineEdit in (self.forenameLineEdit, self.surnameLineEdit,
                self.checkNumLineEdit, self.accountNumLineEdit,
                self.bankLineEdit, self.sortCodeLineEdit,
                self.creditCardLineEdit):
            lineEdit.textEdited.connect(self.updateUi)
        for dateEdit in (self.validFromDateEdit, self.expiryDateEdit):
            dateEdit.dateChanged.connect(self.updateUi)

        self.paidCheckBox.clicked.connect(self.updateUi)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        self.updateUi()
        self.setWindowTitle("Payment Form")


    def updateUi(self):
        today = QDate.currentDate()
        enable = (self.forenameLineEdit.text() and
                       self.surnameLineEdit.text())
        if enable:
            enable = (self.paidCheckBox.isChecked() or
                  (self.checkNumLineEdit.text() and
                        self.accountNumLineEdit.text() and
                        self.bankLineEdit.text() and
                        self.sortCodeLineEdit.text()) or
                  (self.creditCardLineEdit.text() and
                   self.validFromDateEdit.date() <= today and
                   self.expiryDateEdit.date() >= today))
        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(bool(enable))
Example #7
0
class MainWindow(QWidget):

    articleInfoUpdate = pyqtSignal()
    entityUpdate = pyqtSignal()
    crawlerUpdate = pyqtSignal()

    def __init__(self):
        super(MainWindow, self).__init__()

        self.initUI()

        self.articleInfoUpdate.connect(self.updateArticleInfo)
        self.entityUpdate.connect(self.updateEntityList)

    def initUI(self):
        self.setGeometry(0, 0, 500, 700)

        self.center()
        self.setWindowTitle('PView')

        mainLayout = QVBoxLayout()

        self.createArticleInfoBox()
        self.createViewArticleBox()
        self.createEntityBox()
        self.createReportBox()
        self.createDatabaseBox()

        mainLayout.addWidget(self.infoBox)
        mainLayout.addWidget(self.viewArticleBox)
        mainLayout.addWidget(self.entityBox)
        mainLayout.addWidget(self.raportBox)
        mainLayout.addWidget(self.databaseBox)

        self.setLayout(mainLayout)

        self.show()

    def createArticleInfoBox(self):
        self.articleCount = self.selectCountArticles()
        entityCount = self.selectCountEntities()
        associationsCount = self.selectCountAssociations()
        classifiedCount = self.selectCountClassifiedAssociations()

        label = "Number of articles: " + str(self.articleCount)
        self.articleCountLabel = QLabel(label)

        label = "Number of entities: " + str(entityCount)
        self.entitiesCountLabel = QLabel(label)

        label = "Number of associations: " + str(associationsCount)
        self.associationsCountLabel = QLabel(label)

        label = "Number of classified associations: " + str(classifiedCount)
        self.classifiedCountLabel = QLabel(label)

        layout = QVBoxLayout()
        layout.addWidget(self.articleCountLabel)
        layout.addWidget(self.entitiesCountLabel)
        layout.addWidget(self.associationsCountLabel)
        layout.addWidget(self.classifiedCountLabel)

        self.infoBox = QGroupBox("Statistics")
        self.infoBox.setLayout(layout)

    def createCrawlerBox(self):
        self.crawlButton = QPushButton("Crawl")
        self.crawlButton.setFocusPolicy(Qt.NoFocus)

        self.websiteList = QComboBox()
        self.websiteList.addItem("HotNews")

        layout = QGridLayout()

        layout.addWidget(self.websiteList, 0, 0, 1, 1)
        layout.addWidget(self.crawlButton, 0, 1, 1, 1)

        layout.setColumnStretch(0, 1)
        layout.setColumnStretch(1, 1)

        self.crawlerBox = QGroupBox("Crawler")
        self.crawlerBox.setLayout(layout)

    def createViewArticleBox(self):
        self.articleNumberLineEdit = QLineEdit("")
        self.articleNumberLineEdit.setAlignment(Qt.AlignHCenter)

        self.viewArticleButton = QPushButton("Open")
        self.viewArticleButton.clicked.connect(self.viewArticle)

        layout = QGridLayout()

        layout.addWidget(self.articleNumberLineEdit, 0, 0, 1, 1)
        layout.addWidget(self.viewArticleButton, 0, 1, 1, 1)

        layout.setColumnStretch(0, 1)
        layout.setColumnStretch(1, 1)

        self.viewArticleBox = QGroupBox("View Article")
        self.viewArticleBox.setLayout(layout)

    def createReportBox(self):
        minDate, maxDate = self.selectMinAndMaxDate()
        minDate = QDate(minDate.year, minDate.month, minDate.day)
        maxDate = QDate(maxDate.year, maxDate.month, maxDate.day)

        self.fromDateEdit = QDateEdit()
        self.fromDateEdit.setDate(minDate)
        self.fromDateEdit.setDisplayFormat('d MMM yyyy')
        self.fromDateEdit.setAlignment(Qt.AlignHCenter)

        self.toDateEdit = QDateEdit()
        self.toDateEdit.setDate(maxDate)
        self.toDateEdit.setDisplayFormat('d MMM yyyy')
        self.toDateEdit.setAlignment(Qt.AlignHCenter)

        self.reportTypeComboBox = QComboBox()
        for item in reportTypes:
            self.reportTypeComboBox.addItem(item)

        monthlyButton = QPushButton("View")
        monthlyButton.clicked.connect(self.createReport)

        layout = QGridLayout()

        layout.addWidget(self.fromDateEdit,         0, 0, 1, 1)
        layout.addWidget(self.toDateEdit,           0, 1, 1, 1)
        layout.addWidget(self.reportTypeComboBox,   1, 0, 1, 1)
        layout.addWidget(monthlyButton,             1, 1, 1, 1)

        layout.setColumnStretch(0, 1)
        layout.setColumnStretch(1, 1)

        self.raportBox = QGroupBox("Charts")
        self.raportBox.setLayout(layout)

    def createEntityBox(self):
        rows = self.selectCountEntities()

        self.entityList = QListWidget()
        entities = self.selectAllEntities()
        for entity in entities:
            self.doAssociationsForEntity(entity[1])
            self.entityList.addItem(entity[1])

        addButton = QPushButton("Add")
        addButton.clicked.connect(self.addEntity)

        removeButton = QPushButton("Delete")
        removeButton.clicked.connect(self.removeEntity)

        self.addEntityLineEdit = QLineEdit("")
        viewArticlesButton = QPushButton("View articles")
        viewArticlesButton.clicked.connect(self.viewArticleByEntity)

        self.algorithmComboBox = QComboBox()
        for key in classifiers.keys():
            self.algorithmComboBox.addItem(key)

        classifyButton = QPushButton("Classify")
        classifyButton.clicked.connect(self.classifyAllAssociations)

        layout = QGridLayout()
        layout.addWidget(self.entityList,           0, 0, 1, 4)
        layout.addWidget(self.addEntityLineEdit,    1, 0, 1, 2)
        layout.addWidget(addButton,                 1, 2, 1, 1)
        layout.addWidget(removeButton,              1, 3, 1, 1)
        layout.addWidget(viewArticlesButton,        2, 0, 1, 4)
        layout.addWidget(self.algorithmComboBox,    3, 0, 1, 2)
        layout.addWidget(classifyButton,            3, 2, 1, 2)

        layout.setColumnStretch(0, 1)
        layout.setColumnStretch(1, 1)
        layout.setColumnStretch(2, 1)
        layout.setColumnStretch(3, 1)

        self.entityBox = QGroupBox("Entities")
        self.entityBox.setLayout(layout)

    def createDatabaseBox(self):
        deleteClassificationsButton = QPushButton("Remove all classifications")
        deleteClassificationsButton.clicked.connect(self.clearAllCalculatedPolarities)

        deleteEntitiesButton = QPushButton("Remove all entities")
        deleteEntitiesButton.clicked.connect(self.deleteAllEntities)

        deleteAssociationsButton = QPushButton("Remove all associations")
        deleteAssociationsButton.clicked.connect(self.deleteAllAssociations)

        layout = QVBoxLayout()
        layout.addWidget(deleteClassificationsButton)
        layout.addWidget(deleteAssociationsButton)
        layout.addWidget(deleteEntitiesButton)

        self.databaseBox = QGroupBox("Database")
        self.databaseBox.setLayout(layout)

    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def monthsBetweenDates(self, fromDate, toDate):
        curDate = QDate(fromDate)
        months =[]

        while curDate < toDate:
            months.append(curDate)
            curDate = curDate.addMonths(1)

        return months

    def makeMonthlyPolarityChart(self, entities, fromDate, toDate):
        cursor = mysql_conn.cursor()

        chartData = []

        for (entityId, entity) in entities:
            monthlyPol = self.selectAllPolaritiesForEntity(entityId, fromDate, toDate)

            trace0=Bar(
                x = [self.monthYearLabel(month) for (month, _, _) in monthlyPol],
                y = [(0.0 + rows.count(1L)) / (l+1) * 100 for (_, l, rows) in monthlyPol],
                name = entity,
                marker = Marker(
                        color = 'rgb(204,204,204)',
                        opacity = 0.5,
                ),
            )
            chartData.append(trace0)

        chartData = Data(chartData)
        layout = Layout( 
            xaxis=XAxis(
                #set x-axis' labels direction at 45 degree angle
                tickangle=-45,
                ),
            barmode='group',
        )
        fig = Figure(data = chartData, layout = layout)
        py.image.save_as({'data': chartData}, "polarities.png")
        img = Image.open("polarities.png")
        img.show()

    def makeMonthlyAppearanceChart(self, entities, fromDate, toDate):
        cursor = mysql_conn.cursor()

        chartData = []

        for (entityId, entity) in entities:
            monthlyApp = self.selectCountAssociationsForEntityBetweenDates(entityId, fromDate, toDate)

            trace0=Bar(
                x = [self.monthYearLabel(month) for (month, _) in monthlyApp],
                y = [count for (_, count) in monthlyApp],
                name = entity,
                marker = Marker(
                        color = 'rgb(204,204,204)',
                        opacity = 0.5,
                ),
            )
            chartData.append(trace0)

        chartData = Data(chartData)
        layout = Layout( 
            xaxis=XAxis(
                #set x-axis' labels direction at 45 degree angle
                tickangle=-45,
                ),
            barmode='group',
        )
        fig = Figure(data = chartData, layout = layout)
        py.image.save_as({'data': chartData}, "appearances.png")
        img = Image.open("appearances.png")
        img.show()

    def getStringDate(self, date):
        sDate = str(date.year())
        sDate += '-'+str(date.month())
        sDate += '-'+'01'
        time = '00:00:00'
        return sDate + ' ' + time

    def monthYearLabel(self, date):
        label = monthIntToString[date.month()] + ' '
        label += str(date.year())
        return label

    def createReport(self):
        reportType = self.reportTypeComboBox.currentText()
        fromDate = self.fromDateEdit.date()
        toDate = self.toDateEdit.date()
        entities = []

        if "All entities" in reportType:
            entities = self.selectAllEntities()
        else:
            selected = self.entityList.selectedItems()
            if len(selected) == 1:
                entity = selected[0].text()
                entities = [(self.selectEntityId(entity), entity)]

        if len(entities) > 0:
            if "Opinions" in reportType:
                self.makeMonthlyPolarityChart(entities, fromDate, toDate)
            else:
                print entities
                self.makeMonthlyAppearanceChart(entities, fromDate, toDate)

    def viewArticle(self):
        try:
            articleId = int(self.articleNumberLineEdit.text())
            if articleId > 0 and articleId < self.articleCount:
                self.viewArticleButton.setEnabled(False)
                self.articleNumberLineEdit.setDisabled(True)

                articleList = [i+1 for i in xrange(self.articleCount)]
                articleView = Article(articleId-1, articleList, parentW=self)
                articleView.exec_()

                self.viewArticleButton.setEnabled(True)
                self.articleNumberLineEdit.setDisabled(False)
        except ValueError:
            print "Invalid article id"

    def viewArticleByEntity(self):
        selected = self.entityList.selectedItems()

        if len(selected) == 1:
            articles = self.selectAllArticlesByEntity(selected[0].text())
            articleList = [a[0] for a in articles]
            articleView = Article(0, articleList, shuffle_=True, parentW=self)
            articleView.exec_()

    def addEntity(self):
        newEntity = self.addEntityLineEdit.text().strip()
        newEntity = re.sub(r' +', ' ', newEntity)
        cursor = mysql_conn.cursor()

        if len(newEntity) != 0:
            selectStmt = """SELECT *
                            FROM entities
                            WHERE entity=%s"""
            data = (newEntity,)
            cursor.execute(selectStmt, data)
            rows = cursor.fetchall()

            if len(rows) == 0:
                insertStmt = """INSERT INTO entities (entity)
                                VALUES (%s)"""
                data = (newEntity,)
                cursor.execute(insertStmt, data)
                cursor.execute("""COMMIT""")

                self.entityUpdate.emit()
                self.doAssociationsForEntity(newEntity)
                self.addEntityLineEdit.setText("")

    def removeEntity(self):
        selected = self.entityList.selectedItems()
        cursor = mysql_conn.cursor()

        for item in selected:
            self.deleteAssciationsForEntity(item.text())

            selectStmt = """SELECT entity_id
                            FROM entities
                            WHERE entity=%s"""
            data = (item.text(),)
            cursor.execute(selectStmt, data)
            entityId = cursor.fetchone()

            deleteStmt = """DELETE FROM entities
                            WHERE entity_id=%s"""
            data = (entityId[0],)
            cursor.execute(deleteStmt, data)
            cursor.execute("""COMMIT""")

            self.entityUpdate.emit()

    def updateEntityList(self):
        self.entityList.clear()
        entities = self.selectAllEntities()
        for entity in entities:
            self.entityList.addItem(entity[1])

        label = "Number of entities: " + str(len(entities))
        self.entitiesCountLabel.setText(label)

    def updateArticleInfo(self):
        self.articleCount = self.selectCountArticles()
        entityCount = self.selectCountEntities()
        associationsCount = self.selectCountAssociations()
        classifiedCount = self.selectCountClassifiedAssociations()

        label = "Number of articles: " + str(self.articleCount)
        self.articleCountLabel.setText(label)

        label = "Number of entities: " + str(entityCount)
        self.entitiesCountLabel.setText(label)

        label = "Number of classified associations: " + str(classifiedCount)
        self.classifiedCountLabel.setText(label)

        label = "Number of associations: " + str(associationsCount)
        self.associationsCountLabel.setText(label)

    def classifyAllAssociations(self):
        cursor = mysql_conn.cursor()

        entities = self.selectAllEntities()
        for (entityId, entity) in entities:
            manualPol = self.selectManualPolaritiesForEntity(entityId)
            trainingData = [self.selectArticle(id_)[4] for (id_, _) in manualPol]
            trainingTarget = [polarity for (_, polarity) in manualPol]

            algorithm = self.algorithmComboBox.currentText()
            textClf = Pipeline([('vect', CountVectorizer()),
                                ('tfidf', TfidfTransformer()),
                                ('clf', classifiers[algorithm]),
                                ])
            textClf.fit(trainingData, trainingTarget)

            # select all articles associated with entity that need to be classified
            selectStmt = """SELECT article_id
                            FROM assocEntityArticle
                            WHERE polarity_manual IS NULL
                            AND polarity_calculated IS NULL
                            AND entity_id=%s"""
            data = (entityId,)
            cursor.execute(selectStmt, data)
            ids = cursor.fetchall()

            if len(ids) > 0:
                ids = [a[0] for a in ids]
                testData = [self.selectArticle(id_)[4] for id_ in ids]

                predicted = textClf.predict(testData)
                print [x for x in predicted].count(1)
                updateData = zip(predicted, ids)
                updateData = [(polarity, entityId, id_) for (polarity, id_) in updateData]

                updateStmt = """UPDATE assocEntityArticle
                                SET polarity_calculated=%s
                                WHERE entity_id=%s AND article_id=%s"""
                cursor.executemany(updateStmt, updateData)
                cursor.execute("""COMMIT""")

                self.articleInfoUpdate.emit()

    def selectArticle(self, articleId):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT *
                        FROM articles
                        WHERE article_id=%s"""
        data = (articleId,)
        cursor.execute(selectStmt, data)
        row = cursor.fetchone()

        return row

    def selectEntityId(self, entity):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT entity_id
                FROM entities
                WHERE entity=%s"""
        data = (entity,)
        cursor.execute(selectStmt, data)
        entityId = cursor.fetchone()[0]

        return entityId

    def selectAllArticlesByEntity(self, entity):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT *
                        FROM articles
                        WHERE content LIKE %s"""
        data = ("%" + entity + "%",)
        cursor.execute(selectStmt, data)
        rows = cursor.fetchall()

        return rows

    def selectAllEntities(self):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT *
                        FROM entities"""
        cursor.execute(selectStmt)
        rows = cursor.fetchall()

        return rows

    def selectMinAndMaxDate(self):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT MIN(date), MAX(date)
                        FROM articles"""
        cursor.execute(selectStmt)
        row = cursor.fetchone()

        return row

    def selectCountArticles(self):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT count(*)
                        FROM articles"""
        cursor.execute(selectStmt)
        row = cursor.fetchone()

        return row[0]

    def selectCountAuthors(self):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT count(*)
                        FROM authors"""
        cursor.execute(selectStmt)
        row = cursor.fetchone()

        return row[0]

    def selectCountEntities(self):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT count(*)
                        FROM entities"""
        cursor.execute(selectStmt)
        row = cursor.fetchone()

        return row[0]

    def selectCountAssociations(self):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT count(*)
                        FROM assocEntityArticle"""
        cursor.execute(selectStmt)
        row = cursor.fetchone()

        return row[0]

    def selectCountAssociationsForEntityBetweenDates(self, entityId, fromDate, toDate):
        cursor = mysql_conn.cursor()
        months = self.monthsBetweenDates(fromDate, toDate)

        selectStmt = """SELECT count(*)
                        FROM assocEntityArticle a, articles b
                        WHERE a.article_id = b.article_id
                        AND b.date BETWEEN %s AND %s
                        AND a.entity_id=%s"""
        associations = []
        if len(months) != 0:
            for month in months:
                fromDateString = self.getStringDate(month)
                toDateString = self.getStringDate(month.addMonths(1))
                data = (fromDateString, toDateString, entityId)
                cursor.execute(selectStmt, data)
                count = cursor.fetchone()[0]
                associations.append((month, count))

        return associations

    def selectCountClassifiedAssociations(self):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT count(*)
                        FROM assocEntityArticle
                        WHERE polarity_calculated IS NOT NULL
                        OR polarity_manual IS NOT NULL"""
        cursor.execute(selectStmt)
        row = cursor.fetchone()

        return row[0]

    def selectManualPolaritiesForEntity(self, entityId):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT article_id, polarity_manual
                        FROM assocEntityArticle
                        WHERE polarity_manual IS NOT NULL
                        AND entity_id=%s"""
        data = (entityId,)
        cursor.execute(selectStmt, data)
        rows = cursor.fetchall()

        return rows

    def selectAllPolaritiesForEntity(self, entityId, fromDate, toDate):
        cursor = mysql_conn.cursor()
        months = self.monthsBetweenDates(fromDate, toDate)

        selectStmt = """SELECT a.polarity_manual, a.polarity_calculated
                        FROM assocEntityArticle a, articles b
                        WHERE (a.polarity_manual IS NOT NULL
                            OR a.polarity_calculated IS NOT NULL)
                        AND a.article_id = b.article_id
                        AND b.date BETWEEN %s AND %s
                        AND a.entity_id=%s"""

        polarities = []
        if len(months) != 0:
            for month in months:
                fromDateString = self.getStringDate(month)
                toDateString = self.getStringDate(month.addMonths(1))
                data = (fromDateString, toDateString, entityId)
                cursor.execute(selectStmt, data)
                rows = cursor.fetchall()
                rows = [a or b for a, b in rows]
                polarities.append((month, len(rows), rows))

        return polarities

    def doAssociationsForEntity(self, entity):
        cursor = mysql_conn.cursor()

        # select entity_id for entity given as parameter
        entityId = self.selectEntityId(entity)

        # select list of article_id for which associations exist
        # in database for entity given as param
        selectStmt = """SELECT article_id
                        FROM assocEntityArticle
                        WHERE entity_id=%s"""
        data = (entityId,)
        cursor.execute(selectStmt, data)
        articleIdsInDB = cursor.fetchall()
        articleIdsInDB = [pair[0] for pair in articleIdsInDB]

        # select all articles that contain entity in content
        selectStmt = """SELECT article_id
                        FROM articles
                        WHERE content LIKE %s"""
        data = ("%" + entity + "%",)
        cursor.execute(selectStmt, data)
        rows = cursor.fetchall()
        rows = [pair[0] for pair in rows]

        # find articles for which associations don't exist in the database
        diff = list(set(rows) - set(articleIdsInDB))
        if len(diff) != 0:
            insertStmt = """INSERT INTO assocEntityArticle (article_id, entity_id)
                            VALUES (%s, %s)"""
            data = [(articleId, entityId) for articleId in diff]
            cursor.executemany(insertStmt, data)
            cursor.execute("""COMMIT""")

            self.articleInfoUpdate.emit()

    def deleteAssciationsForEntity(self, entity):
        cursor = mysql_conn.cursor()

        selectStmt = """SELECT entity_id
                        FROM entities
                        WHERE entity=%s"""
        data = (entity,)
        cursor.execute(selectStmt, data)
        entityId = cursor.fetchone()[0]

        deleteStmt = """DELETE FROM assocEntityArticle
                        WHERE entity_id=%s"""
        data = (entityId,)
        cursor.execute(deleteStmt, data)
        cursor.execute("""COMMIT""")

        self.articleInfoUpdate.emit()

    def doAllAssociations(self):
        cursor = mysql_conn.cursor()

        entities = self.selectAllEntities()
        for entity in entities:
            self.doAssociationsForEntity(entity)

        self.articleInfoUpdate.emit()

    def deleteAllAssociations(self):
        cursor = mysql_conn.cursor()

        deleteStmt = """DELETE FROM assocEntityArticle
                        WHERE article_id > 0"""
        cursor.execute(deleteStmt)
        cursor.execute("""COMMIT""")

        self.articleInfoUpdate.emit()

    def clearAllCalculatedPolarities(self):
        cursor = mysql_conn.cursor()

        updateStmt = """UPDATE assocEntityArticle
                        SET polarity_calculated=%s
                        WHERE polarity_calculated IS NOT NULL"""
        data = (None,)
        cursor.execute(updateStmt, data)
        cursor.execute("""COMMIT""")

        self.articleInfoUpdate.emit()

    def deleteAllArticles(self):
        try:
            cursor = mysql_conn.cursor()

            deleteStmt = """DELETE FROM articles
                            WHERE article_id > 0"""
            cursor.execute(deleteStmt)
            alterTableStmt = """ALTER TABLE articles AUTO_INCREMENT = 1"""
            cursor.execute(alterTableStmt)
            cursor.execute("""COMMIT""")

            self.articleInfoUpdate.emit()
        except IntegrityError:
            pass

    def deleteAllAuthors(self):
        cursor = mysql_conn.cursor()

        deleteStmt = """DELETE FROM authors
                        WHERE author_id > 0"""
        cursor.execute(deleteStmt)
        alterTableStmt = """ALTER TABLE authors AUTO_INCREMENT = 1"""
        cursor.execute(alterTableStmt)
        cursor.execute("""COMMIT""")

    def deleteAllArticlesAndAuthors(self):
        self.deleteAllArticles()
        self.deleteAllAuthors()

    def deleteAllEntities(self):
        cursor = mysql_conn.cursor()

        deleteStmt = """DELETE FROM entities
                        WHERE entity_id > 0"""
        cursor.execute(deleteStmt)
        alterTableStmt = """ALTER TABLE entities AUTO_INCREMENT = 1"""
        cursor.execute(alterTableStmt)
        cursor.execute("""COMMIT""")

        self.articleInfoUpdate.emit()
        self.entityUpdate.emit()
Example #8
0
class BuildingServicesClient(QWidget):
    def __init__(self, parent=None):
        super(BuildingServicesClient, self).__init__(parent)

        self.socket = QTcpSocket()
        self.nextBlockSize = 0
        self.request = None

        roomLabel = QLabel("&Room")
        self.roomEdit = QLineEdit()
        roomLabel.setBuddy(self.roomEdit)
        regex = QRegExp(r"[0-9](?:0[1-9]|[12][0-9]|3[0-4])")
        self.roomEdit.setValidator(QRegExpValidator(regex, self))
        self.roomEdit.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        dateLabel = QLabel("&Date")
        self.dateEdit = QDateEdit()
        dateLabel.setBuddy(self.dateEdit)
        self.dateEdit.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
        self.dateEdit.setDate(QDate.currentDate().addDays(1))
        self.dateEdit.setDisplayFormat("yyyy-MM-dd")
        responseLabel = QLabel("Response")
        self.responseLabel = QLabel()
        self.responseLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken)

        self.bookButton = QPushButton("&Book")
        self.bookButton.setEnabled(False)
        self.unBookButton = QPushButton("&Unbook")
        self.unBookButton.setEnabled(False)
        self.bookingsOnDateButton = QPushButton("Bookings &on Date?")
        self.bookingsForRoomButton = QPushButton("Bookings &for Room?")
        self.bookingsForRoomButton.setEnabled(False)
        quitButton = QPushButton("&Quit")
        if not MAC:
            self.bookButton.setFocusPolicy(Qt.NoFocus)
            self.unBookButton.setFocusPolicy(Qt.NoFocus)
            self.bookingsOnDateButton.setFocusPolicy(Qt.NoFocus)
            self.bookingsForRoomButton.setFocusPolicy(Qt.NoFocus)

        buttonLayout = QHBoxLayout()
        buttonLayout.addWidget(self.bookButton)
        buttonLayout.addWidget(self.unBookButton)
        buttonLayout.addWidget(self.bookingsOnDateButton)
        buttonLayout.addWidget(self.bookingsForRoomButton)
        buttonLayout.addStretch()
        buttonLayout.addWidget(quitButton)
        layout = QGridLayout()
        layout.addWidget(roomLabel, 0, 0)
        layout.addWidget(self.roomEdit, 0, 1)
        layout.addWidget(dateLabel, 0, 2)
        layout.addWidget(self.dateEdit, 0, 3)
        layout.addWidget(responseLabel, 1, 0)
        layout.addWidget(self.responseLabel, 1, 1, 1, 3)
        layout.addLayout(buttonLayout, 2, 0, 1, 5)
        self.setLayout(layout)

        self.socket.connected.connect(self.sendRequest)
        self.socket.readyRead.connect(self.readResponse)
        self.socket.disconnected.connect(self.serverHasStopped)
        #self.connect(self.socket,
        #             SIGNAL("error(QAbstractSocket::SocketError)"),
        #            self.serverHasError)
        self.socket.error.connect(self.serverHasError)

        self.roomEdit.textEdited.connect(self.updateUi)
        self.dateEdit.dateChanged.connect(self.updateUi)

        self.bookButton.clicked.connect(self.book)
        self.unBookButton.clicked.connect(self.unBook)
        self.bookingsOnDateButton.clicked.connect(self.bookingsOnDate)
        self.bookingsForRoomButton.clicked.connect(self.bookingsForRoom)
        quitButton.clicked.connect(self.close)

        self.setWindowTitle("Building Services")

    def updateUi(self):
        enabled = False
        if (self.roomEdit.text()
                and self.dateEdit.date() > QDate.currentDate()):
            enabled = True
        if self.request is not None:
            enabled = False
        self.bookButton.setEnabled(enabled)
        self.unBookButton.setEnabled(enabled)
        self.bookingsForRoomButton.setEnabled(enabled)

    def closeEvent(self, event):
        self.socket.close()
        event.accept()

    def book(self):
        self.issueRequest("BOOK", self.roomEdit.text(), self.dateEdit.date())

    def unBook(self):
        self.issueRequest("UNBOOK", self.roomEdit.text(), self.dateEdit.date())

    def bookingsOnDate(self):
        self.issueRequest("BOOKINGSONDATE", self.roomEdit.text(),
                          self.dateEdit.date())

    def bookingsForRoom(self):
        self.issueRequest("BOOKINGSFORROOM", self.roomEdit.text(),
                          self.dateEdit.date())

    def issueRequest(self, action, room, date):
        self.request = QByteArray()
        stream = QDataStream(self.request, QIODevice.WriteOnly)
        stream.setVersion(QDataStream.Qt_5_7)
        stream.writeUInt16(0)
        stream.writeQString(action)
        stream.writeQString(room)
        stream << date
        stream.device().seek(0)
        stream.writeUInt16(self.request.size() - SIZEOF_UINT16)
        self.updateUi()
        if self.socket.isOpen():
            self.socket.close()
        self.responseLabel.setText("Connecting to server...")
        self.socket.connectToHost("localhost", PORT)

    def sendRequest(self):
        self.responseLabel.setText("Sending request...")
        self.nextBlockSize = 0
        self.socket.write(self.request)
        self.request = None

    def readResponse(self):
        stream = QDataStream(self.socket)
        stream.setVersion(QDataStream.Qt_5_7)

        while True:
            if self.nextBlockSize == 0:
                if self.socket.bytesAvailable() < SIZEOF_UINT16:
                    break
                self.nextBlockSize = stream.readUInt16()
            if self.socket.bytesAvailable() < self.nextBlockSize:
                break
            action = ""
            room = ""
            date = QDate()
            action = stream.readQString()
            room = stream.readQString()
            if action == "BOOKINGSFORROOM":
                dates = []
                for x in range(stream.readInt32()):
                    stream >> date
                    dates.append(str(date.toString(Qt.ISODate)))
                dates = ", ".join(dates)
            if action not in ("BOOKINGSFORROOM", "ERROR"):
                stream >> date
            if action == "ERROR":
                msg = "Error: {0}".format(room)
            elif action == "BOOK":
                msg = "Booked room {0} for {1}".format(
                    room, date.toString(Qt.ISODate))
            elif action == "UNBOOK":
                msg = "Unbooked room {0} for {1}".format(
                    room, date.toString(Qt.ISODate))
            elif action == "BOOKINGSONDATE":
                msg = "Rooms booked on {0}: {1}".format(
                    date.toString(Qt.ISODate), room)
            elif action == "BOOKINGSFORROOM":
                msg = "Room {0} is booked on: {1}".format(room, dates)
            self.responseLabel.setText(msg)
            self.updateUi()
            self.nextBlockSize = 0

    def serverHasStopped(self):
        self.responseLabel.setText("Error: Connection closed by server")
        self.socket.close()

    def serverHasError(self, error):
        self.responseLabel.setText("Error: {0}".format(
            self.socket.errorString()))
        self.socket.close()