Exemple #1
0
class frmReportes( QPrintPreviewDialog ):
    """
    Este es un formulario generico que muestra los reportes web generados para las 
    """
    def __init__( self, web, printer , parent = None ):
        """
        @param web: La dirección web a la que apunta el reporte
        @param printer: El objeto QPrinter en el que se imprime, esto es usado por si se desea alguna configuración especifica del reporte
        """
        super( frmReportes, self ).__init__( printer, parent )

        base = Reports.url

        if base == "":
            raise UserWarning( u"No existe una configuración para el servidor de reportes" )


        self.report = base + web + "&uname=" + user.LoggedUser.user + "&hash=" + user.LoggedUser.hash
        self.webview = QWebView()
        self.setWindowFlags( self.windowFlags() | Qt.WindowMaximizeButtonHint )




        self.txtSearch = QLineEdit()

        self.loaded = False


        self.webview.load( QUrl( self.report ) )
        self.progressbar = QProgressBar( self )




        self.paintRequested[QPrinter].connect( self.reprint )
        self.webview.loadFinished[bool].connect( self.on_webview_loadFinished )
        self.webview.loadProgress[int].connect( self.on_webview_loadProgress )



    def reprint( self, printer ):
        self.webview.print_( printer )

    def on_webview_loadProgress( self, progress ):
        self.progressbar.setValue( progress )

    def showEvent( self, _event ):
        if not self.loaded:
            self.progressbar.show()

    def on_webview_loadFinished( self, status ):
        if self.progressbar.isVisible():
            self.progressbar.hide()
        if not status:
            QMessageBox.critical( self, qApp.organizationName(), "El reporte no se pudo cargar" )
            logging.error( "No se pudo cargar el reporte: %s" % self.report )
            self.accept()

        self.loaded = True

        w = self.findChild( QPrintPreviewWidget )
        w.updatePreview()
class MainWindow(KXmlGuiWindow):

    "Class which displays the main Danbooru Client window."

    def __init__(self, *args):

        "Initialize a new main window."

        super(MainWindow, self).__init__(*args)
        self.cache = KPixmapCache("danbooru")
        self.preferences = preferences.Preferences()
        self.api = None
        self.__ratings = None
        self.__step = 0

        self.url_list = self.preferences.boards_list
        self.max_retrieve = self.preferences.thumbnail_no

        self.statusbar = self.statusBar()
        self.progress = QProgressBar()
        self.thumbnailarea = None
        self.tag_dock = None
        self.pool_dock = None
        self.first_fetch_widget = None

        self.progress.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        # FIXME: Hackish, but how to make it small otherwise?
        self.progress.setMinimumSize(100, 1)
        self.statusbar.addPermanentWidget(self.progress)
        self.progress.hide()

        self.setup_welcome_widget()
        self.setup_actions()

    def reload_config(self):
        """Reload configuration after a change"""

        urls = self.preferences.boards_list

        if self.first_fetch_widget is not None:
            self.first_fetch_widget.setup_urls(urls)

        if self.thumbnailarea is not None:

            max_thumbnail = self.preferences.thumbnail_no
            max_rating = self.preferences.max_allowed_rating

            self.thumbnailarea.fetchwidget.limit = max_thumbnail
            self.thumbnailarea.fetchwidget.rating = max_rating
            self.thumbnailarea.fetchwidget.update_values()

            self.thumbnailarea.connectwidget.setup_urls(urls)

        self.url_list = self.preferences.boards_list
        self.max_retrieve = self.preferences.thumbnail_no

    def setup_welcome_widget(self):
        """Load the welcome widget at startup."""

        widget = QWidget()
        layout = QVBoxLayout()

        welcome = QLabel(parent=self)
        pix = QPixmap(KStandardDirs.locate("appdata", "logo.png"))

        welcome.setPixmap(pix)
        welcome.setAlignment(Qt.AlignCenter)

        self.first_fetch_widget = connectwidget.ConnectWidget(
            self.preferences.boards_list, self)

        self.statusbar.addPermanentWidget(self.first_fetch_widget, 300)

        self.first_fetch_widget.connectionEstablished.connect(
            self.handle_connection)
        self.first_fetch_widget.rejected.connect(self.first_fetch_widget.hide)

        self.first_fetch_widget.hide()

        self.first_fetch_widget.setSizePolicy(QSizePolicy.Preferred,
                                              QSizePolicy.Fixed)

        layout.addWidget(self.first_fetch_widget)
        layout.addWidget(welcome)
        widget.setLayout(layout)

        self.setCentralWidget(widget)

    def setup_tooltips(self):
        """Set tooltips for the actions."""

        self.connect_action.setToolTip(i18n("Connect to a Danbooru board"))
        self.fetch_action.setToolTip(
            i18n("Fetch thumbnails from a Danbooru board"))
        self.batch_download_action.setToolTip(i18n("Batch download images"))

    def create_actions(self):
        """Create actions for the main window."""

        self.connect_action = KAction(KIcon("document-open-remote"),
                                      i18n("Connect"), self)
        self.fetch_action = KAction(KIcon("download"), i18n("Download"), self)
        self.clean_action = KAction(KIcon("trash-empty"),
                                    i18n("Clear thumbnail cache"), self)
        self.batch_download_action = KAction(KIcon("download"),
                                             i18n("Batch download"), self)
        self.pool_toggle_action = KToggleAction(KIcon("image-x-generic"),
                                                i18n("Pools"), self)
        self.tag_display_action = KDualAction(i18n("Show tags"),
                                              i18n("Hide tags"), self)
        self.tag_display_action.setIconForStates(KIcon("image-x-generic"))
        self.tag_display_action.setEnabled(False)

        # Shortcuts
        connect_default = KAction.ShortcutTypes(KAction.DefaultShortcut)
        connect_active = KAction.ShortcutTypes(KAction.ActiveShortcut)

        self.connect_action.setShortcut(KStandardShortcut.open())
        self.fetch_action.setShortcut(KStandardShortcut.find())

        self.fetch_action.setEnabled(False)
        self.batch_download_action.setEnabled(False)
        self.pool_toggle_action.setEnabled(False)

    def setup_action_collection(self):
        """Set up the action collection by adding the actions."""

        action_collection = self.actionCollection()

        # Addition to the action collection
        action_collection.addAction("connect", self.connect_action)
        action_collection.addAction("fetch", self.fetch_action)
        action_collection.addAction("clean", self.clean_action)
        action_collection.addAction("batchDownload",
                                    self.batch_download_action)
        action_collection.addAction("poolDownload", self.pool_toggle_action)
        action_collection.addAction("tagDisplay", self.tag_display_action)

        KStandardAction.quit(self.close, action_collection)
        KStandardAction.preferences(self.show_preferences, action_collection)

        action_collection.removeAction(
            action_collection.action("help_contents"))
        action_collection.actionHovered.connect(self.setup_action_tooltip)

    def setup_actions(self):
        """Set up the relevant actions, tooltips, and load the RC file."""

        self.create_actions()
        self.setup_tooltips()
        self.setup_action_collection()

        # Connect signals
        self.connect_action.triggered.connect(self.connect)
        self.fetch_action.triggered.connect(self.get_posts)
        self.clean_action.triggered.connect(self.clean_cache)
        self.batch_download_action.triggered.connect(self.batch_download)
        self.pool_toggle_action.toggled.connect(self.pool_toggle)
        self.tag_display_action.activeChanged.connect(self.tag_display)

        window_options = self.StandardWindowOption(self.ToolBar | self.Keys
                                                   | self.Create | self.Save
                                                   | self.StatusBar)

        setupGUI_args = [
            QSize(500, 400),
            self.StandardWindowOption(window_options)
        ]

        #Check first in standard locations for danbooruui.rc

        rc_file = KStandardDirs.locate("appdata", "danbooruui.rc")

        if rc_file.isEmpty():
            setupGUI_args.append(os.path.join(sys.path[0], "danbooruui.rc"))
        else:
            setupGUI_args.append(rc_file)

        self.setupGUI(*setupGUI_args)

    def setup_action_tooltip(self, action):

        "Show statusbar help when actions are hovered."

        if action.isEnabled():
            self.statusBar().showMessage(action.toolTip(), 2000)

    def setup_connections(self):
        """Set up connections for post and tag retrieval."""

        if self.api is None:
            return

        self.api.postRetrieved.connect(self.update_progress)
        self.api.postDownloadFinished.connect(self.download_finished)
        self.api.tagRetrieved.connect(self.tag_dock.widget().add_tags)
        self.tag_dock.widget().itemDoubleClicked.connect(
            self.fetch_tagged_items)

    def show_preferences(self):

        "Show the preferences dialog."

        if KConfigDialog.showDialog("Preferences dialog"):
            return
        else:
            dialog = preferences.PreferencesDialog(self, "Preferences dialog",
                                                   self.preferences)
            dialog.show()
            dialog.settingsChanged.connect(self.reload_config)

    def connect(self, ok):

        "Connect to a Danbooru board."

        if self.thumbnailarea is None:
            self.first_fetch_widget.show()
        else:
            self.thumbnailarea.connectwidget.show()

    def restore(self):

        self.statusbar.removeWidget(self.connect_widget)

    def handle_connection(self, connection):

        self.api = None
        self.api = connection
        self.api.cache = self.cache

        if self.pool_dock is not None:
            self.pool_dock.hide()
            self.pool_dock.widget().clear()
            self.pool_toggle_action.setChecked(False)

        if self.thumbnailarea is not None:
            #TODO: Investigate usability
            self.clear(clear_pool=True)
            self.thumbnailarea.clear()
            self.thumbnailarea.api_data = self.api
            self.setup_connections()

        else:
            self.first_fetch_widget.connectionEstablished.disconnect()
            self.first_fetch_widget.rejected.disconnect()
            self.statusbar.removeWidget(self.first_fetch_widget)
            self.setup_area()

        self.api.cache = self.cache

        self.statusBar().showMessage(i18n("Connected to %s" % self.api.url),
                                     3000)
        self.fetch_action.setEnabled(True)

        # Set up pool widget

        pool_widget = poolwidget.DanbooruPoolWidget(self.api)
        self.pool_dock = QDockWidget("Pools", self)
        self.pool_dock.setObjectName("PoolDock")
        self.pool_dock.setAllowedAreas(Qt.BottomDockWidgetArea)
        self.pool_dock.setWidget(pool_widget)
        #self.pool_dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(Qt.BottomDockWidgetArea, self.pool_dock)
        self.pool_dock.widget().poolDownloadRequested.connect(
            self.pool_prepare)
        self.pool_dock.hide()

        self.pool_toggle_action.setEnabled(True)
        self.clear()  # Needed to show properly the stuff after connecting

        self.api.get_post_list(tags="",
                               limit=self.thumbnailarea.post_limit,
                               rating=self.preferences.max_allowed_rating,
                               blacklist=list(self.preferences.tag_blacklist))
        self.api.get_tag_list(name="",
                              blacklist=list(self.preferences.tag_blacklist),
                              limit=20)

    def get_posts(self, ok):

        "Get posts from the connected Danbooru board."

        if not self.api:
            return

        self.thumbnailarea.fetchwidget.show()

    def handle_fetching(self, tags, max_rating, limit):
        """Slot connected to the dataSent signal of the fetch widget.

        The widgets are set up if they don't exist, and the API is queried
        to do the actual downloading of tags and

        """

        self.clear()

        self.thumbnailarea.fetchwidget.hide()

        if self.tag_dock is not None:
            self.tag_dock.widget().clear()

        self.thumbnailarea.post_limit = limit
        blacklist = list(self.preferences.tag_blacklist)
        self.api.get_post_list(tags=tags,
                               limit=limit,
                               rating=max_rating,
                               blacklist=blacklist)

        tags = [item for item in tags if item]

        if not tags:
            # No related tags, fetch the most recent 20
            tags = ""
            self.api.get_tag_list(name=tags, blacklist=blacklist, limit=20)
        else:
            self.api.get_related_tags(tags=tags, blacklist=blacklist)

    def fetch_tagged_items(self, item):
        """Fetch items found in the tag list widget."""

        tag_name = unicode(item.text())
        self.clear()

        blacklist = self.preferences.tag_blacklist
        limit = self.preferences.thumbnail_no
        rating = self.preferences.max_allowed_rating

        self.api.get_post_list(page=1,
                               tags=[tag_name],
                               blacklist=blacklist,
                               limit=limit,
                               rating=rating)
        self.api.get_related_tags(tags=[tag_name], blacklist=blacklist)

    def pool_toggle(self, checked):

        "Toggle the presence/absence of the pool dock."

        if not self.api:
            return

        if not checked:
            self.pool_dock.hide()
        else:
            self.pool_dock.show()

    def pool_prepare(self, pool_id):
        """Prepare the central area for pool image loading."""

        if self.thumbnailarea is None:
            self.setup_area()
        else:
            self.clear(clear_pool=False)

        self.api.get_pool(pool_id,
                          blacklist=self.preferences.tag_blacklist,
                          rating=self.preferences.max_allowed_rating)

    def batch_download(self, ok):

        "Download images in batch."

        selected_items = self.thumbnailarea.selected_images()

        if not selected_items:
            return

        start_url = KUrl("kfiledialog:///danbooru")
        caption = i18n("Select a directory to save the images to")
        directory = KFileDialog.getExistingDirectoryUrl(
            start_url, self, caption)

        if directory.isEmpty():
            return

        for item in selected_items:

            file_url = item.url_label.url()
            tags = item.data.tags

            # Make a local copy to append paths as addPath works in-place
            destination = KUrl(directory)

            file_name = KUrl(file_url).fileName()
            destination.addPath(file_name)

            job = KIO.file_copy(KUrl(file_url), destination, -1)
            job.setProperty("tags", QVariant(tags))
            job.result.connect(self.batch_download_slot)

    def setup_area(self):

        "Set up the central widget to display thumbnails."

        self.thumbnailarea = thumbnailarea.DanbooruTabWidget(
            self.api, self.preferences, self.preferences.thumbnail_no, self)

        self.setCentralWidget(self.thumbnailarea)

        self.thumbnailarea.connectwidget.connectionEstablished.connect(
            self.handle_connection, type=Qt.UniqueConnection)
        self.thumbnailarea.connectwidget.rejected.connect(
            self.thumbnailarea.connectwidget.hide, type=Qt.UniqueConnection)

        self.thumbnailarea.fetchwidget.dataSent.connect(
            self.handle_fetching, type=Qt.UniqueConnection)
        self.thumbnailarea.fetchwidget.rejected.connect(
            self.thumbnailarea.fetchwidget.hide, type=Qt.UniqueConnection)

        # Set up tag widget

        blacklist = self.preferences.tag_blacklist
        tag_widget = tagwidget.DanbooruTagWidget(blacklist, self)
        self.tag_display_action.setActive(True)
        self.tag_display_action.setEnabled(True)
        self.tag_dock = QDockWidget("Similar tags", self)
        self.tag_dock.setObjectName("TagDock")
        self.tag_dock.setAllowedAreas(Qt.RightDockWidgetArea)
        self.tag_dock.setWidget(tag_widget)
        #self.tag_dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(Qt.RightDockWidgetArea, self.tag_dock)
        self.tag_dock.hide()

        # Container signal-slot connections

        self.setup_connections()

    def download_finished(self):
        """Slot called when all the data has been completed. Clears the progress
        bar and resets it to 0."""

        if not self.batch_download_action.isEnabled():
            self.batch_download_action.setEnabled(True)

        self.__step = 0
        self.progress.hide()

    def update_progress(self):

        "Update the progress bar."

        if not self.progress.isVisible():
            self.progress.show()

        self.__step += 1
        self.progress.setValue(self.__step)

    def clear(self, clear_pool=True):

        "Clear the central widget."

        if self.thumbnailarea is None:
            return

        self.thumbnailarea.clear()
        self.tag_dock.widget().clear()
        if clear_pool:
            self.pool_dock.widget().clear()
        self.batch_download_action.setEnabled(False)

    def clean_cache(self):

        "Purge the thumbnail cache."

        self.cache.discard()
        self.statusBar().showMessage(i18n("Thumbnail cache cleared."))

    def batch_download_slot(self, job):
        """Slot called when doing batch download, for each file retrieved.

        If Nepomuk tagging is enabled, each file is tagged using the item's
        respective tags.

        """

        if job.error():
            job.ui().showErrorMessage()
        else:
            if self.preferences.nepomuk_enabled:
                tags = job.property("tags").toPyObject()
                #danbooru2nepomuk.tag_danbooru_item(job.destUrl().path(),
                #                                   tags)

    def tag_display(self, state):
        """Display or hide the tag dock."""

        if self.tag_dock is None:
            self.tag_display_action.setActive(False)
            return

        if state:
            self.tag_dock.show()
        else:
            self.tag_dock.hide()
Exemple #3
0
class SegmentationClassificationWindow(SoundLabWindow, Ui_MainWindow):
    """

    Window that process the segmentation and classification of a signal
    Contains a QSignalDetectorWidget that wrapper several functionality
    Allows to select the segmentation and classification settings,
    and parameter measurement for detected segments.
    Provides a table for visualization of segment and measures,
    A two dimensional window to graph two measured params. One for each axis.
    Options for selection and visualization of segments
    Provides options for save the parameters to excel.
    """

    # region CONSTANTS

    # sort type of the parameter table
    PARAMETER_SORT_NONE, PARAMETER_SORT_BY_LOCATION, PARAMETER_SORT_BY_PARAMETER = range(
        3)

    # different colors for the even and odds rows in the parameter table and segment colors.
    TABLE_ROW_COLOR_ODD = QColor(0, 0, 255, 150)
    TABLE_ROW_COLOR_EVEN = QColor(0, 255, 0, 150)

    # stylesheet to use on excel file saved
    EXCEL_STYLE_HEADER = xlwt.easyxf(
        'font: name Times New Roman, color-index black, bold on, height 300')
    EXCEL_STYLE_BODY = xlwt.easyxf(
        'font: name Times New Roman, color-index black, height 220',
        num_format_str='#,# # 0.00')
    EXCEL_STYLE_COPYRIGHT = xlwt.easyxf(
        'font: name Arial, color-index pale_blue, height 250, italic on',
        num_format_str='# ,# # 0.00')

    # endregion

    # region Initialize

    def __init__(self, parent=None, signal=None, workspace=None):
        """
        Create a the window of segmentation and classification.
        :param parent:    the parent widget if any
        :param signal:    the signal to visualize for segmentation and classification
        :param workspace: the workspace with specgram and oscilogram generation options
        :return:
        """

        # set the visual variables and methods from ancestor's
        SoundLabWindow.__init__(self, parent)
        self.setupUi(self)

        # check the parameters
        if signal is None or not isinstance(signal, AudioSignal):
            raise Exception(
                "The signal to analyze must be of type AudioSignal")

        self.configureToolBarActionsGroups()

        # the segmentation window do not allow to record a signal (for now...)
        self.actionRecord.setEnabled(False)

        # the object that handles the measuring of parameters and manage the segments
        self.segmentManager = SegmentManager()
        self.segmentManager.measurementsFinished.connect(
            self.measurement_finished)
        self.segmentManager.detectionProgressChanged.connect(
            lambda x: self.windowProgressDetection.setValue(x * 0.9))
        self.segmentManager.segmentVisualItemAdded.connect(
            self.widget.add_parameter_visual_items)
        self.segmentManager.segmentationFinished.connect(
            self.segmentation_finished)

        # the factory of adapters for parameters to supply to the ui window or segmentation dialog
        self.parameter_manager = MeasurementTemplate(signal=signal)

        # set the signal to the widget
        self.widget.signal = signal
        self.segmentManager.signal = signal

        # set the configurations for the name of the signal and the visible label
        self.updateSignalPropertiesLabel(signal)
        self.signalNameLineEdit.setReadOnly(True)
        self.actionSignalName.setText(self.widget.signalName)

        # set visible the two graphs by default
        self.changeWidgetsVisibility(True, True)

        # shift of the table parameters rows. If the table visualization isn't expanded
        # the first row are the name of the parameters and the second one the locations
        self.parameter_table_row_shift = 2

        # connect the signals on the widget for new detected data by its tools
        # and to select the element in the table. Binding the element click to the table
        self.widget.toolDataDetected.connect(self.update_status_bar)
        self.widget.elementClicked.connect(self.select_element)

        self.tableParameterOscilogram.setSelectionBehavior(
            QAbstractItemView.SelectRows)
        self.tableParameterOscilogram.setEditTriggers(
            QAbstractItemView.NoEditTriggers)
        self.dockWidgetParameterTableOscilogram.setVisible(False)
        self.tableParameterOscilogram.setSortingEnabled(False)

        # connect the table parameter sort type actions
        self.actionGroupNone.triggered.connect(
            self.groupTableParametersChanged)
        self.actionGroupByParameter.triggered.connect(
            self.groupTableParametersChanged)
        self.actionGroupByLocation.triggered.connect(
            self.groupTableParametersChanged)

        # configuration variable for the parameter table visualization sort
        self.parameter_table_sort_type = self.PARAMETER_SORT_BY_PARAMETER

        # add the context menu actions to the widget
        self.__addContextMenuActions()

        # create the progress bar that is showed while the detection is made
        self.windowProgressDetection = QProgressBar(self)
        self.set_progress_bar_visibility(False)

        # array of windows with two dimensional graphs
        self.two_dim_windows = []
        self._cross_correlation_windows = []

        if workspace is not None:
            self.load_workspace(workspace)

        if parent is not None:
            # show window in the center of the parent
            parent_x, parent_y = parent.x(), parent.y()
            parent_width, parent_height = parent.width(), parent.height()
            a = str('a')
            window_x = parent_x + (parent_width - self.width()) / 2
            window_y = parent_y + (parent_height - self.height()) / 2
            self.move(window_x, window_y)

        self.show()

        self.try_restore_previous_session()

    def try_restore_previous_session(self):
        """
        Restore (if any) the previous session with this file.
        That means detected elements, measured parameters etc that are saved on the signal
        extra data.
        :return:
        """
        elements = self.widget.get_signal_segmentation_data()

        if len(elements) == 0:
            return

        buttons_box = QMessageBox.Yes | QMessageBox.No
        mbox = QMessageBox(
            QMessageBox.Question, self.tr(u"soundLab"),
            self.
            tr(u"The file has segmentation data stored. Do you want to load it?"
               ), buttons_box, self)
        result = mbox.exec_()

        if result == QMessageBox.Yes:
            for i, e in enumerate(elements):
                self.segmentManager.add_element(i, e[0], e[1])

            self.widget.elements = self.segmentManager.elements
            self.segmentManager.measure_parameters_and_classify()
            self.widget.graph()

    def __addContextMenuActions(self):
        """
        Add the context menu actions into the widget in the creation process of the window
        :return:
        """
        separator, separator1, separator2, separator3, separator4 = [
            QAction(self) for _ in xrange(5)
        ]

        for sep in [separator, separator1, separator2, separator3, separator4]:
            sep.setSeparator(True)

        # region Context Menu Actions

        self.widget.createContextCursor([
            # parameters manipulation actions
            self.actionMeditions,
            self.actionView_Parameters,
            self.actionAddElement,
            separator2,

            # Zoom Actions
            self.actionZoomIn,
            self.actionZoom_out,
            self.actionZoom_out_entire_file,
            separator1,

            # widgets visibility actions
            self.actionCombined,
            self.actionOscilogram,
            self.actionSpectogram,
            separator,

            # change tools actions
            self.actionZoom_Cursor,
            self.actionPointer_Cursor,
            self.actionRectangular_Cursor,
            separator3,

            # elements select/deselect
            self.actionDeselect_Elements,
            self.actionDelete_Selected_Elements,
            self.actionSelectedElement_Correlation,
            self.actionClassify,
            separator4
        ])
        # endregion
        # add the action to switch visualization mode on  the parameter table
        self.tableParameterOscilogram.setContextMenuPolicy(
            QtCore.Qt.ActionsContextMenu)

        parameter_tables_action_group = QActionGroup(
            self.tableParameterOscilogram)
        actions = [
            self.actionGroupNone, self.actionGroupByLocation,
            self.actionGroupByParameter
        ]

        for act in actions:
            parameter_tables_action_group.addAction(act)
            self.tableParameterOscilogram.addAction(act)

    def configureToolBarActionsGroups(self):
        """
        :return:
        """
        sep = QAction(self)
        sep.setSeparator(True)

        # region Segmentation and Transformations actions
        segm_transf_actions = QActionGroup(self)
        segm_transf_actions_list = [
            self.actionDetection, self.actionTwo_Dimensional_Graphs,
            self.actionDelete_Selected_Elements, self.actionDeselect_Elements,
            self.actionAddElement, self.actionParameter_Measurement, sep
        ]

        for act in segm_transf_actions_list:
            act.setActionGroup(segm_transf_actions)

        # endregion

        # add to the customizable sound lab toolbar first than the default actions
        # addActionGroup(actionGroup, name)
        self.toolBar.addActionGroup(segm_transf_actions,
                                    u"Segments and Parameters")

        # apply the common sound lab window toolbar actions
        SoundLabWindow.configureToolBarActionsGroups(self)

    # endregion

    # region Two Dimensional Graphs

    @pyqtSlot()
    def on_actionTwo_Dimensional_Graphs_triggered(self):
        """
        Creates a new two dimensional window for analysis.
        :return:
        """
        # a two dim window must be created after
        # segment detection and parameters measurement
        if self.segmentManager.rowCount == 0 or len(
                self.segmentManager.parameterColumnNames) == 0:
            QMessageBox.warning(
                QMessageBox(), self.tr(u"Error"),
                self.tr(
                    u"The two dimensional analyses window requires at least "
                    u"one detected element with one parameter measurement."))
            return

        wnd = TwoDimensionalAnalisysWindow(self, self.segmentManager)

        # connect the signals for update the new two dim window actions
        wnd.elementSelected.connect(self.select_element)
        wnd.elementsClassified.connect(
            lambda indexes_list, classification: self.segmentManager.
            set_manual_elements_classification(indexes_list, classification))

        # load the workspace in the new two dimensional window
        if self.workSpace:
            wnd.load_workspace(self.workSpace)

        # if any previous windows was opened then update in the new one the selected element
        if len(self.two_dim_windows) > 0:
            wnd.select_element(self.two_dim_windows[0].selectedElementIndex)

        # add the new window to the current opened windows
        self.two_dim_windows.append(wnd)

    def clear_two_dim_windows(self):
        """
        Close the two dimensional windows and clear the list of two dim windows
        :return:
        """
        # close the opened windows
        for window in self.two_dim_windows:
            window.close()
            del window

        # clear the list of two dimensional windows
        self.two_dim_windows = []

    # endregion

    # region Save Measurements

    @pyqtSlot()
    def on_actionMeditions_triggered(self):
        """
        Save to disc the measurement made by the window to the elements detected.
        :param name: The name of the file to save the data
        :param table: The table with the parameter to save into excel
        :return: True if successfully False otherwise
        """
        file_name = unicode(
            QFileDialog.getSaveFileName(
                self, self.tr(u"Save parameters as excel file"),
                os.path.join(self.workSpace.lastOpenedFolder,
                             str(self.widget.signalName) + ".xls"), "*.xls"))
        # save the data of table
        if not file_name:
            return False

        # if successfully selected the path save the excel book of data
        try:

            wb = xlwt.Workbook()
            ws = wb.add_sheet("Elements Measurements")
            self.write_data(ws, self.tableParameterOscilogram)
            wb.save(file_name)

            self.segmentManager.save_data_on_db()

        except Exception as ex:
            print("Error saving the excel file. " + ex.message)
            return False

        # open user manual file
        os.system("start " + file_name)
        return True

    @pyqtSlot()
    def on_actionSound_File_Segmentation_triggered(self):
        """
        Save the signal into file. Store the segmentation values
        (list of tuples start,end with the indexes of detected element)
        :return:
        """
        if self.segmentManager.rowCount == 0:
            return

        self.widget.save_segments_into_signal()

        # save the signal to file
        if self.widget.signalFilePath:
            self.widget.save()
        else:
            file_name = unicode(
                QFileDialog.getSaveFileName(
                    self, self.tr(u"Save signal"),
                    os.path.join(self.workSpace.lastOpenedFolder,
                                 str(self.widget.signalName)), u"*.wav"))
            if file_name:
                self.widget.save(file_name)
                self.widget.signalFilePath = file_name

    def set_signal_file(self, file_path=''):
        """
        Update the data of the current signal file path origin in the widget
        (if any)
        :param file_path:
        :return:
        """
        self.widget.signalFilePath = file_path

    def write_data(self, ws, table_parameter):
        """
        Write the data from the table into an excel file stylesheet.
        :param ws:WorkSheet object from xwlt module for interacts with excell files.
        :param table_parameter: QTableWidget with the information of the data to save.
        """
        # write headers into the document
        headers = []
        for pos in xrange(table_parameter.columnCount()):
            header_item = table_parameter.takeHorizontalHeaderItem(pos)
            if header_item is not None:
                headers.append(str(header_item.text()))

        for index, header in enumerate(headers):
            ws.write(0, index, header, self.EXCEL_STYLE_HEADER)

        # write data into the document
        for i in xrange(1, table_parameter.model().rowCount() + 1):
            for j in xrange(table_parameter.model().columnCount()):
                item = table_parameter.item(i - 1, j)
                if item is not None:
                    cell_data = str(item.data(Qt.DisplayRole).toString())

                    ws.write(i, j, cell_data, self.EXCEL_STYLE_BODY)

        # ws object must be part of a Work book that would be saved later
        ws.write(table_parameter.model().rowCount() + 3, 0,
                 unicode(self.tr(u"duetto-Sound Lab")),
                 self.EXCEL_STYLE_COPYRIGHT)

    # endregion

    # region Close and Exit

    @pyqtSlot()
    def on_actionExit_triggered(self):
        """
        Process the exit action requested by the user.
        :return:
        """
        self.close()

    def closeEvent(self, event):
        """
        Event that manages the close of the window.
        Intercepts the close event for save changes.
        :param event:
        :return:
        """

        mbox = QMessageBox(
            QMessageBox.Question, self.tr(u"Save measurements"),
            self.tr(u"Do you want to save the parameters of " +
                    unicode(self.widget.signalName)),
            QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, self)

        # if the signal was playing must be stopped
        self.widget.stop()
        self.segmentManager.stop_processing()

        # if there is a measurement made and parameters measured that could be saved
        if self.tableParameterOscilogram.rowCount() > 0:

            result = mbox.exec_()

            # check if the user decision about save is discard and continue working
            if result == QMessageBox.Cancel or (
                    result == QMessageBox.Yes
                    and not self.on_actionMeditions_triggered()):
                event.ignore()
                return

            self.clear_two_dim_windows()

    # endregion

    # region Elements Selection

    @pyqtSlot()
    def on_actionDelete_Selected_Elements_triggered(self):
        """
        Delete the element under selection.
        the selection is the area under the zoom cursor if the zoom cursor is selected
        or the visible area otherwise.
        :return:
        """

        # delete the elements on the widget and get the indexes for update
        deleted_elements = self.widget.selected_elements_interval()

        self.widget.delete_selected_elements()

        if deleted_elements is None:
            return

        start_removed_index, end_removed_index = deleted_elements

        if start_removed_index is not None and start_removed_index >= 0 \
           and end_removed_index < self.segmentManager.rowCount:

            # updates the detected elements
            self.segmentManager.delete_elements(start_removed_index,
                                                end_removed_index)

            for wnd in self.two_dim_windows:
                wnd.update_data(self.segmentManager)

        # deselect the elements on the widget
        self.on_actionDeselect_Elements_triggered()

    @pyqtSlot()
    def on_actionDelete_All_triggered(self):
        """
        Removes all the detected elements on the widget
        :return:
        """
        # clear the segments
        self.segmentManager.elements = []

        # removes the widget elements
        self.widget.elements = []
        self.widget.graph()

    @pyqtSlot()
    def on_actionAddElement_triggered(self):
        """
        Try to add the selected region on the widget as a new element
        Performs the manual segmentation.
        :return:
        """
        element_added_index = self.widget.mark_region_as_element()

        if element_added_index is None:
            return

        element = self.widget.get_element(element_added_index)

        self.segmentManager.add_element(element_added_index, element.indexFrom,
                                        element.indexTo)

        self.update_two_dim_windows()

        self.widget.graph()

        self.__set_parameter_window_visible()

    @pyqtSlot()
    def on_actionDeselect_Elements_triggered(self):
        """
        Deselects the selected element in the widget and in the
        two dimensional windows opened.
        :return:
        """
        self.widget.deselect_element()

        for wnd in self.two_dim_windows:
            wnd.deselect_element()

        # the table remains equal as before for efficiency (do not update for deselect)

    def select_element(self, element_index, column=0):
        """
        Callback that is executed for update the element that is selected.
        An element is selected by the user ad must be updated in all visible representations
        like table parameter, two dimensional windows, and graphs.
        :param element_index: index of the element selected
        :param column: parameter provided to reuse this method as callback of
        the event selected cell in the QTableWidget. Useless in this application context.
        """
        # select the element in the table of parameters
        self.tableParameterOscilogram.selectRow(element_index +
                                                self.parameter_table_row_shift)

        # in the widget...
        self.widget.select_element(element_index)

        # in the opened two dimensional windows...
        for wnd in self.two_dim_windows:
            wnd.select_element(element_index)

        # and in the cross-correlation windows
        for i in xrange(len(self._cross_correlation_windows)):
            wnd = self._cross_correlation_windows[i]
            if wnd.isHidden():
                del self._cross_correlation_windows[i]
            else:
                wnd.select_element(element_index)

        # show the image if the element is classified
        try:
            classification = self.segmentManager.get_segment_classification(
                element_index)
            image = classification.get_image()
            if image:
                self.show_image_on_element(element_index, image)

            self.update_status_bar(classification.get_full_description())

        except Exception as ex:
            pass

    @pyqtSlot()
    def on_actionSelectedElement_Correlation_triggered(self):
        signal = self.widget.selected_element_signal()
        if not signal:
            QMessageBox.warning(QMessageBox(), self.tr(u"Error"),
                                self.tr(u"There is no selected element."))
            return

        dialog = CrossCorrelationDialog(self, self.widget, signal,
                                        self.TABLE_ROW_COLOR_ODD,
                                        self.TABLE_ROW_COLOR_EVEN)

        self._cross_correlation_windows.append(dialog)
        dialog.elementSelected.connect(self.select_element)
        dialog.show()

    @pyqtSlot()
    def on_actionClassify_triggered(self):
        """
        Open the classification dialog for update the categories and values
        in which could be classified a segment.
        """
        # create and open the dialog to edit the classification categories
        selection = self.widget.selected_elements_interval()

        if selection is None:
            QMessageBox.warning(QMessageBox(), self.tr(u"Warning"),
                                self.tr(u"There is no selection made."))
            return

        index_from, index_to = selection
        classification_dialog = ManualClassificationDialog()

        if classification_dialog.exec_():
            classification = classification_dialog.get_classification()
            self.segmentManager.set_manual_elements_classification(
                xrange(index_from, index_to + 1), classification)

    # endregion

    # region Classification

    def show_image_on_element(self, element_index, image):
        """
        show a toast with the specie image (if any and if is identified) of
        the element at element index position
        :param element_index:
        :return:
        """
        toast = ToastWidget(self, back_image=image, width=100, heigth=100)

        element = self.segmentManager.elements[element_index]
        min_x, max_x = self.widget.get_visible_region()

        x = element.indexFrom - min_x + (element.indexTo -
                                         element.indexFrom) / 2.0
        x = x * self.widget.width() * 1.0 / (max_x - min_x)

        toast.move(
            self.widget.mapToGlobal(
                QPoint(x - toast.width() / 2.0,
                       (self.widget.height() - toast.height()) / 2.0)))

        toast.disappear()

    @pyqtSlot()
    def on_actionCross_correlation_triggered(self):
        """
        Opens the cross-correlation dialog (after selecting a file containing a reference segment) to check each
        segment's match with a reference segment.
        """
        file_name = QFileDialog.getOpenFileName(
            self,
            self.tr(u"Select the file of a reference segment"),
            directory=self.workSpace.lastOpenedFile,
            filter=self.tr(u"Wave Files") + u"(*.wav);;All Files(*)")
        if file_name:
            dialog = CrossCorrelationDialog(self, self.widget, file_name,
                                            self.TABLE_ROW_COLOR_ODD,
                                            self.TABLE_ROW_COLOR_EVEN)

            self._cross_correlation_windows.append(dialog)
            dialog.elementSelected.connect(self.select_element)
            dialog.show()

    # endregion

    # region Detection

    def set_progress_bar_visibility(self, visibility=True):
        """
        Show the progress bar in the middle of the widget.
        Used when a high time demanding task is going to be made to
        show to the user it's progress.
        :return:
        """
        if not visibility:
            self.windowProgressDetection.setVisible(False)

        else:
            width, height = self.width(), self.height()
            x, y = self.widget.x(), self.widget.y()
            progress_bar_height = height / 20.0

            self.windowProgressDetection.resize(width / 3.0,
                                                progress_bar_height)
            self.windowProgressDetection.move(
                x + width / 3.0, y + height / 2.0 + progress_bar_height / 2.0)
            self.windowProgressDetection.setVisible(True)

        self.repaint()

    @pyqtSlot()
    def on_actionDetection_triggered(self):
        """
        Method that execute the detection
        """

        # there is an on going detection been made
        if self.windowProgressDetection.isVisible():
            QMessageBox.warning(
                QMessageBox(), self.tr(u"Error"),
                self.tr(u"There is an on going detection in progress."))
            return

        elementsDetectorDialog = ElemDetectSettingsDialog(
            self, self.widget.signal, self.segmentManager)
        elementsDetectorDialog.modifyParametersMeasurement.connect(
            lambda: self.on_actionParameter_Measurement_triggered())
        elementsDetectorDialog.load_workspace(self.workSpace)

        try:
            if elementsDetectorDialog.exec_():
                # the detection dialog is a factory of segmentation,
                # parameter parameters and classification concrete implementations

                # get the segmentation, classification and parameters methods
                self.segmentManager.detector_adapter = elementsDetectorDialog.detector
                self.segmentManager.classifier_adapter = elementsDetectorDialog.classifier
                self.segmentManager.parameters = self.parameter_manager.parameter_list(
                )

                self.windowProgressDetection.setValue(0)
                self.set_progress_bar_visibility(True)

                # execute the detection
                self.segmentManager.detect_elements()

        except Exception as e:
            print("detection errors: " + e.message)

            self.windowProgressDetection.setValue(100)
            self.set_progress_bar_visibility(False)

    def segmentation_finished(self):
        """
        Callback to execute when the segmentation segmentation_thread finished.
        :return:
        """
        self.windowProgressDetection.setValue(90)

        # put the elements detected into the widget to visualize them
        self.widget.elements = self.segmentManager.elements

        # shows the detection elements first and later the measurements as lazy load
        self.widget.draw_elements()

        self.windowProgressDetection.setValue(100)
        self.set_progress_bar_visibility(False)

        # measure the parameters over elements detected
        QTimer.singleShot(50,
                          self.segmentManager.measure_parameters_and_classify)

    def measurement_finished(self):
        """
        Callback to execute when the measurement has finished.
        :return:
        """
        # must be refreshed the widget because the parameter measurement
        # may include visual items into the graph
        self.widget.draw_elements()
        self.update_two_dim_windows()
        self.update_parameter_table()
        self.__set_parameter_window_visible()

    def __set_parameter_window_visible(self):
        # set the parameter window to visible state after measurements
        if not self.tableParameterOscilogram.isVisible():
            self.actionView_Parameters.setChecked(True)
            self.dockWidgetParameterTableOscilogram.setVisible(True)

    def update_two_dim_windows(self):
        # update the measured data on the two dimensional opened windows
        for wnd in self.two_dim_windows:
            wnd.load_data(self.segmentManager)

    def groupTableParametersChanged(self):

        grouped_params = self.actionGroupByParameter.isChecked()
        grouped_locations = self.actionGroupByLocation.isChecked()
        grouped_none = self.actionGroupNone.isChecked()

        self.parameter_table_row_shift = 0 if grouped_none else 2

        if grouped_none:
            self.parameter_table_sort_type = self.PARAMETER_SORT_NONE

        elif grouped_params:
            self.parameter_table_sort_type = self.PARAMETER_SORT_BY_PARAMETER

        elif grouped_locations:
            self.parameter_table_sort_type = self.PARAMETER_SORT_BY_LOCATION

        self.update_parameter_table()
        self.on_actionDeselect_Elements_triggered()

    # endregion

    # region Parameter Table

    def _group_measurements(self, parameters):
        """
        group the measurements for display in table
        :param parameters: the parameters measurements to group
        :return: list of groups (list) grouped by location or parameter
        accord to the instance variable  parameter_table_sort_type
        """
        groups = []

        def group_item_selector(param):
            if self.parameter_table_sort_type == self.PARAMETER_SORT_BY_LOCATION:
                return "-" if param.time_location is None else param.time_location.name
            return param.name

        param_positions = [(parameters[i], i) for i in xrange(len(parameters))]

        comparer = lambda x, y: cmp(group_item_selector(x[0]),
                                    group_item_selector(y[0]))

        param_positions.sort(cmp=comparer)

        index = 0
        while index < len(param_positions):
            current_group = []
            current_item = group_item_selector(param_positions[index][0])

            while index < len(parameters) and group_item_selector(
                    param_positions[index][0]) == current_item:
                current_group.append(param_positions[index][0])
                current_item = group_item_selector(param_positions[index][0])
                index += 1

            groups.append(current_group)

        return groups, [t[1] for t in param_positions]

    def _get_table_widget_item_centered(self, text):
        """
        return a TableWidgetItem with the text supplied and centered vertical and  horizontally
        :param text: text of the item
        :return:
        """
        item = QTableWidgetItem(unicode(text))
        item.setTextAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        return item

    def __fill_parameter_table_measurements(self, param_indexes):
        """
        Fill the values of the table parameters using the param indexes
        from the parameter sort defined by user. Is a helper method of the update parameter table
        :param param_indexes:
        :return:
        """

        # update every measurement [row, column] position
        for i in xrange(self.segmentManager.rowCount):
            for j in xrange(self.segmentManager.columnCount):
                # the last columns aren't for parameters but for classification
                param_index = param_indexes[j] if j < len(param_indexes) else j

                item = self._get_table_widget_item_centered(
                    self.segmentManager[i, param_index])

                # color options for the rows of the table
                item.setBackgroundColor(self.TABLE_ROW_COLOR_ODD if
                                        (i + self.parameter_table_row_shift) %
                                        2 == 0 else self.TABLE_ROW_COLOR_EVEN)

                self.tableParameterOscilogram.setItem(
                    i + self.parameter_table_row_shift, j, item)

    def __fill_parameter_table_grouped_columns_headers(self, params_groups,
                                                       indexes):
        # set the span value to group the param names columns and their names
        column_index = 0

        parameters = []
        for g in params_groups:
            parameters.extend(g)

        for j in xrange(len(parameters)):
            if self.parameter_table_sort_type == self.PARAMETER_SORT_BY_LOCATION:
                text = parameters[j].name
            else:
                text = "-" if parameters[j].time_location is None else unicode(
                    parameters[j].time_location.name)

            item = self._get_table_widget_item_centered(text)
            self.tableParameterOscilogram.setItem(1, j, item)

        # set the group names on the row 0
        for param_group in params_groups:
            span_size = len(param_group)
            if span_size > 1:
                self.tableParameterOscilogram.setSpan(0, column_index, 1,
                                                      span_size)

            if self.parameter_table_sort_type == self.PARAMETER_SORT_BY_PARAMETER:
                text = param_group[0].name
            else:
                text = "-" if param_group[0].time_location is None else unicode(
                    param_group[0].time_location.name)

            item = self._get_table_widget_item_centered(text)
            self.tableParameterOscilogram.setItem(0, column_index, item)
            column_index += span_size

        # put the classification category headers
        for index, category_classif in enumerate(
                self.segmentManager.classificationColumnNames):
            item = self._get_table_widget_item_centered(category_classif)
            self.tableParameterOscilogram.setItem(0, column_index + index,
                                                  item)

    def update_parameter_table(self):
        """
        Method that updates the parameter table to visualize
        the data of the detected segments, their measured parameters and classification
        :return:
        """
        parameters = self.segmentManager.parameters

        self.tableParameterOscilogram.clear()
        self.tableParameterOscilogram.setRowCount(
            self.segmentManager.rowCount + self.parameter_table_row_shift)
        self.tableParameterOscilogram.setColumnCount(
            self.segmentManager.columnCount)

        header_labels = self.segmentManager.columnNames

        if self.parameter_table_sort_type != self.PARAMETER_SORT_NONE:
            # get the list of parameters grouped by type
            params_groups, indexes = self._group_measurements(parameters)

            header_labels = ["" for _ in self.segmentManager.columnNames]

            # set the vertical names of rows. Parameters on first row and locations on second
            vertical_headers = [
                "Parameter" if s == 0 else
                "Location" if s == 1 else str(s -
                                              self.parameter_table_row_shift +
                                              1)
                for s in xrange(self.segmentManager.rowCount +
                                self.parameter_table_row_shift)
            ]

            if self.parameter_table_sort_type == self.PARAMETER_SORT_BY_LOCATION:
                swap = vertical_headers[0]
                vertical_headers[0] = vertical_headers[1]
                vertical_headers[1] = swap

            self.tableParameterOscilogram.setVerticalHeaderLabels(
                vertical_headers)
            self.__fill_parameter_table_grouped_columns_headers(
                params_groups, indexes)
            self.__fill_parameter_table_measurements(indexes)

        else:
            self.__fill_parameter_table_measurements(xrange(len(parameters)))

        self.tableParameterOscilogram.setHorizontalHeaderLabels(header_labels)

        # connect the table selection with the selection of an element
        cell_pressed_element = lambda index, col: self.select_element(
            index - self.parameter_table_row_shift, col)
        self.tableParameterOscilogram.cellPressed.connect(cell_pressed_element)

        self.tableParameterOscilogram.resizeColumnsToContents()
        self.tableParameterOscilogram.resizeRowsToContents()

    # endregion

    # region Visual Elements

    @pyqtSlot()
    def on_actionView_Parameters_triggered(self):
        """
        Changes the visibility on the window of the parameter table.
        The parameter table is where the detected segments and its measured parameters
        are displayed.
        """
        self.dockWidgetParameterTableOscilogram.setVisible(
            self.actionView_Parameters.isChecked())

    @pyqtSlot()
    def on_actionTemporal_Elements_triggered(self):
        """
        Temporal Elements are the elements that are visible on the oscilogram graph.
        This method allows to change its visibility
        """
        visibility = self.actionTemporal_Elements.isChecked()

        self.widget.visual_items_visibility.oscilogram_items_visible = visibility
        self.widget.draw_elements()

        for x in [
                self.actionTemporal_Figures, self.actionTemporal_Numbers,
                self.actionTemporal_Parameters
        ]:
            x.setEnabled(visibility)

    @pyqtSlot()
    def on_actionTemporal_Figures_triggered(self):
        self.widget.visual_items_visibility.oscilogram_figures_visible = self.actionTemporal_Figures.isChecked(
        )
        self.widget.draw_elements()

    @pyqtSlot()
    def on_actionTemporal_Parameters_triggered(self):
        self.widget.visual_items_visibility.oscilogram_parameters_visible = self.actionTemporal_Parameters.isChecked(
        )
        self.widget.draw_elements()

    @pyqtSlot()
    def on_actionTemporal_Numbers_triggered(self):
        """
        Change visibility of the numbers of the detected segments on the oscilogram graph

        """
        self.widget.visual_items_visibility.oscilogram_text_visible = self.actionTemporal_Numbers.isChecked(
        )
        self.widget.draw_elements()

    @pyqtSlot()
    def on_actionSpectral_Elements_triggered(self):
        """
        Spectral Elements are the elements that are visible on the spectrogram graph.
        This method allows to change its visibility
        """
        visibility = self.actionSpectral_Elements.isChecked()

        self.widget.visual_items_visibility.spectrogram_items_visible = visibility
        self.widget.draw_elements()

        for x in [
                self.actionSpectral_Numbers, self.actionSpectral_Figures,
                self.actionSpectral_Parameters
        ]:
            x.setEnabled(visibility)

    @pyqtSlot()
    def on_actionSpectral_Figures_triggered(self):
        self.widget.visual_items_visibility.spectrogram_figures_visible = self.actionSpectral_Figures.isChecked(
        )
        self.widget.draw_elements()

    @pyqtSlot()
    def on_actionSpectral_Parameters_triggered(self):
        self.widget.visual_items_visibility.spectrogram_parameters_visible = self.actionSpectral_Parameters.isChecked(
        )
        self.widget.draw_elements()

    @pyqtSlot()
    def on_actionSpectral_Numbers_triggered(self):
        """
        Change visibility of the numbers of the detected segments on the oscilogram graph

        """
        self.widget.visual_items_visibility.spectrogram_text_visible = self.actionSpectral_Numbers.isChecked(
        )
        self.widget.draw_elements()

    # endregion

    def update_parameters(self, parameter_manager):
        """
        Updates the parameters to measure from a change in their selection.
        :param parameter_manager:
        :return:
        """
        self.widget.release_items(parameter_items=True, elements_items=False)

        # update the segment manager that measure the new params
        self.segmentManager.parameters = parameter_manager.parameter_list()

        self.measurement_finished()

    @pyqtSlot()
    def on_actionParameter_Measurement_triggered(self):

        # check for previously parameters measurements to save
        # there is measured parameters when the list of their names has at least one element
        if len(self.segmentManager.parameterColumnNames) > 0:

            mbox = QMessageBox(
                QMessageBox.Question, self.tr(u"soundLab"),
                self.tr(
                    u"You are going to change the parameters to measure."
                    u"All your previously selected measurements will be lost."
                    u"Do you want to save measurements first?"),
                QMessageBox.Yes | QMessageBox.No, self)

            if mbox.exec_() == QMessageBox.Yes:
                self.on_actionMeditions_triggered()

        param_window = ParametersWindow(self,
                                        self.parameter_manager,
                                        self.widget.signal,
                                        workspace=self.workSpace)

        param_window.parameterChangeFinished.connect(
            lambda p: self.update_parameters(p))

        param_window.show()

    def load_workspace(self, workspace):
        """
        Method that loads the workspace to update visual options from main window.
        :param workspace:
        """
        self.workSpace = workspace
        self.widget.load_workspace(workspace)

        # update workspace on every window
        for wnd in self.two_dim_windows:
            wnd.load_workspace(self.workSpace)
Exemple #4
0
class Base(QMainWindow):
    """
    Esta clase sirve de base para todos aquellos 
    formularios que siguen el estandar de dos pestañas, una para navegación
    y otra para edición
    """

    orientation = QPrinter.Portrait
    pageSize = QPrinter.Letter
    web = ""

    def __init__(self, parent, own_toolbar=False):
        """
        @param parent: El widget padre de esta ventana
        @param own_toolbar: Si este widget dibujara su toolbar en el padre o en el mismo
        @type parent: QWidget
        @type own_toolbar: bool
        """
        super(Base, self).__init__(parent)
        self.user = user.LoggedUser
        self._status = True
        self.parentWindow = parent
        self.own_toolbar = own_toolbar

        self.database = QSqlDatabase.database()
        """
        @type: QSqlDatabase
        @ivar: La base de datos a la cual se conecta el sistema
        """

        self.mapper = QDataWidgetMapper(self)
        u"""
        @type: QDataWidgetMapper
        @ivar: El mapper que se encarga de asignar los 
        datos del modelo de navegación a los distintos widgets
        """

        self.printProgressBar = QProgressBar(self)
        self.webview = QWebView()
        """
        @ivar: EL objeto webview usado para cargar los reportes
        @type: QWebView
        """
        self.loaded = False
        """
        @ivar: Si se pudo o no cargar un reporte
        @type: bool
        """

        self.startUi()

        self.editmodel = None
        self.printer = QPrinter()

        self._status = False

    def startUi(self):
        """
        Iniciar todos los elementos graficos
        """
        self.setupUi(self)
        if not self.own_toolbar:
            self.parent().addToolBar(self.toolBar)
            self.removeToolBar(self.toolBar)

        settings = QSettings()
        self.restoreState(settings.value(self.windowTitle() + "/State").toByteArray())

        """
        @ivar: El MainWindow al que pertenece este widget
        """
        self.createActions()

        self.printProgressBar.setVisible(False)

        _tab1shortcut = QShortcut(QKeySequence("Ctrl+1"), self, functools.partial(self.tabWidget.setCurrentIndex, 0))
        _tab2shortcut = QShortcut(QKeySequence("Ctrl+2"), self, functools.partial(self.tabWidget.setCurrentIndex, 1))

        self.mapper.currentIndexChanged[int].connect(self.updateDetailFilter)

        self.actionCut.setVisible(False)
        self.actionPaste.setVisible(False)
        self.actionCopy.setVisible(False)

    def closeEvent(self, event):
        u"""
        Guardar el tamaño, la posición en la pantalla y la posición de
         la barra de tareas
        Preguntar si realmente se desea cerrar la pestaña cuando
         se esta en modo edición
        """
        if not self.status:
            if (
                not QMessageBox.question(
                    self, qApp.organizationName(), u"¿Está seguro que desea salir?", QMessageBox.Yes | QMessageBox.No
                )
                == QMessageBox.Yes
            ):
                event.ignore()

        # Guardar el tamaño y la posición
        settings = QSettings()
        settings.setValue(self.windowTitle() + "/Geometry", self.saveGeometry())

        if not self.own_toolbar:
            self.parent().mdiArea().parent().parent().removeToolBar(self.toolBar)

    def editCell(self):
        """
        Editar la celda actualmente seleccionada de self.tableedit
        """
        self.tabledetails.edit(self.tabledetails.selectionModel().currentIndex())

    @pyqtSlot(QDateTime)
    @if_edit_model
    def on_dtPicker_dateTimeChanged(self, datetime):
        """
        Cambiar el tipo de cambio del modelo de edición si cambia la fecha
        @param datetime: La fecha contenida en self.dtPicker
        @type datetime: QDateTime
        """
        query = QSqlQuery()
        try:
            if not self.database.isOpen():
                if not self.database.open():
                    raise Exception(
                        "No se pudo conectar a la base de " + "datos para recuperar los tipos " + "de cambio"
                    )

            q = """
                SELECT idtc, tasa
                FROM tiposcambio
                WHERE fecha = %s
                LIMIT 1
            """ % datetime.toString(
                "yyyyMMdd"
            )

            if not query.exec_(q):

                raise UserWarning("No se pudieron recuperar los tipos de " + "cambio")
            if not query.size() == 1:
                logging.critical(u"La consulta para obtener tipos de " + "cambio no devolvio exactamente un valor")
                raise UserWarning(u"Hubo un error al obtener los tipos " + "de cambio")

            query.first()
            self.editmodel.exchangeRateId = query.value(0).toInt()[0]
            self.editmodel.exchangeRate = Decimal(query.value(1).toString())

            # self.editmodel.setData( self.editmodel.index( 0, 0 ), self.editmodel.index( 0, 0 ).data() )

            self.editmodel.datetime = datetime
        except UserWarning as inst:
            QMessageBox.critical(self, qApp.organizationName(), unicode(inst))
            self.dtPicker.setDateTime(self.editmodel.datetime)
            logging.error(inst)
            logging.error(query.lastError().text())
        except Exception as inst:
            QMessageBox.critical(self, qApp.organizationName(), u"Hubo un error al obtener los tipos de" + " cambio")
            logging.critical(query.lastError().text())
            logging.critical(inst)
            self.dtPicker.setDateTime(self.editmodel.datetime)

    def navigate(self, to):
        """
        Esta funcion se encarga de navegar entro los distintos documentos
        @param to: es una string que puede tomar los valores 'next' 'previous'
         'first' 'last'
        """
        if self.mapper.currentIndex != -1:
            row = self.mapper.currentIndex()
            if to == "next":
                row += 1
                if row >= self.navproxymodel.rowCount():
                    row = self.navproxymodel.rowCount() - 1
                self.mapper.setCurrentIndex(row)
            elif to == "previous":
                if row <= 1:
                    row = 0
                else:
                    row = row - 1
                self.mapper.setCurrentIndex(row)
            elif to == "first":
                self.mapper.toFirst()
            elif to == "last":
                self.mapper.toLast()
        else:
            self.mapper.toLast()()

        if self.tabledetails != None:
            self.tabledetails.resizeColumnsToContents()
            self.tabledetails.horizontalHeader().setStretchLastSection(True)
        self.tablenavigation.selectRow(self.mapper.currentIndex())

    def updateDetailFilter(self, _index):
        """
        Esta función se debe implementar en los formularios para que al 
        navegar se actualize el filtro de la tabla detalles
        @param index: Este es el indice del mapper en el que actualmente 
        se encuentra navegando
        @type index: int 
        """
        QMessageBox.information(self, qApp.organizationName(), u"Esta parte del sistema no ha " + "sido implementada")
        raise NotImplementedError()

    def loadModels(self):
        """
        Esta función se ejecuta en el constructor del formulario mediante
        un QTimer, carga los formularios por primera vez
        """
        self.updateModels()

        self.navigate("last")
        self.status = True

    def _setStatus(self, stat):
        """
        @param stat:  False = editando, True = navegando
        @type stat: bool
        """
        self._status = stat
        self.setControls(self._status)

    def _getStatus(self):
        """
        esta propiedad cambia entre navegar y editar
        """
        return self._status

    status = property(_getStatus, _setStatus)

    @pyqtSlot(unicode)
    def on_txtSearch_textChanged(self, searchstring):
        """
        Cambiar el filtro para el navigation model
        @param searchstring: Es el contenido por el cual se va a 
        filtrar el modelo de navegación
        @type searchstring: string
        """
        self.navproxymodel.setFilterFixedString(searchstring)

    @pyqtSlot(QModelIndex)
    def on_tablenavigation_doubleClicked(self, index):
        """
        Al hacer doble click en la tabla de navegación el se cambia a la
        pestaña detalles mostrando el documento seleccionado
        @param index: El indice de la tabla en la que se dio doble click
        @type index: QModelIndex 
        """
        self.mapper.setCurrentIndex(index.row())
        self.tabWidget.setCurrentIndex(0)

    @pyqtSlot(QModelIndex)
    def on_tablenavigation_clicked(self, index):
        self.mapper.setCurrentIndex(index.row())

    def save(self, ask=True):
        """
        Guardar el documento actual
        @param ask: Si se deberia o no preguntar al usuario si 
            esta seguro antes de proceder
        @type ask: bool
        """
        if (
            ask == False
            or QMessageBox.question(
                self, qApp.organizationName(), u"¿Esta seguro que desea guardar?", QMessageBox.Yes | QMessageBox.No
            )
            == QMessageBox.Yes
        ):
            if self.editmodel.valid:
                if self.editmodel.save():
                    QMessageBox.information(self, qApp.organizationName(), u"El documento se ha guardado con éxito")
                    self.editmodel = None
                    self.updateModels()
                    self.navigate("last")
                    self.status = True
                else:
                    QMessageBox.critical(self, qApp.organizationName(), "Ha ocurrido un error al guardar el documento")

            else:
                try:
                    QMessageBox.warning(self, qApp.organizationName(), self.editmodel.validError)
                except AttributeError:
                    QMessageBox.warning(
                        self,
                        qApp.organizationName(),
                        u"El documento no puede guardarse" + " ya que la información no esta" + " completa",
                    )

    def setControls(self, unused_status):
        """
        Habilitar o deshabilitar los controles según status
        @param status: 
        @type status: bool
        """
        QMessageBox.information(self, qApp.organizationName(), u"Esta parte del sistema no ha sido implementada")
        raise NotImplementedError()

    def addLine(self):
        """
        añadir una linea a table edit, solo se llama directamente 
        en una ocasion, al comenzar la edicion de un documento
        """
        row = self.editmodel.rowCount()
        self.editmodel.insertRows(row)

    def createAction(self, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, signal="triggered"):
        """
        Crear un objeto acción
        @param text: El texto de la acción
        @type text: string

        @param slot: El slot que se ejecutara cuando se dispare esta acción
        @type slot: callable

        @param shortcut: El acceso directo que tiene asignada esta acción
        @type shortcut: QKeySequence

        @param icon: El icono de esta acción
        @type icon: string

        @param tip: El tooltip que tendra esta acción
        @type tip: string

        @param checkable: Si esta acción es checkable o no
        @type checkable: bool

        @param signal: La señal en la que esta acción ejecutara el slot
        @type signal: string

        @rtype: QAction
        """
        action = QAction(text, self)
        if icon is not None:
            if type(icon) == QIcon:
                action.setIcon(icon)
            else:
                action.setIcon(QIcon(icon))
        if shortcut is not None:
            action.setShortcut(shortcut)
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        if slot is not None:
            getattr(action, signal).connect(slot)
        if checkable:
            action.setCheckable(True)
        return action

    def newDocument(self):
        """
        Empezar la edición de un nuevo documento
        """
        QMessageBox.information(self, qApp.organizationName(), u"Esta parte del sistema no ha sido implementada")
        raise NotImplementedError()

    def cancel(self):
        """
        Cancelar la edición del nuevo documento
        """
        QMessageBox.information(self, qApp.organizationName(), u"Esta parte del sistema no ha sido implementada")
        raise NotImplementedError()

    @property
    def printIdentifier(self):
        """
        La identificación de este documento para reporte,
         normalmente sera el iddocumento o el ndocimpreso
        @rtype:string
        """
        raise NotImplementedError(u"printIdentifier debe implementarse para " + "poder imprimir")

    def preview(self):
        """
        Muestra el dialogo de vista previa de impresión
        """
        try:
            printer = QPrinter()
            printer.setOrientation(self.orientation)
            printer.setPageSize(self.pageSize)
            web = self.web + self.printIdentifier
            report = reports.frmReportes(web, printer, self)
            report.exec_()
        except NotImplementedError as inst:
            QMessageBox.information(
                self, qApp.organizationName(), u"No se ha implementado la función de impresión para este modulo"
            )
            logging.error(unicode(inst))
        except UserWarning as inst:
            QMessageBox.critical(self, qApp.organizationName(), unicode(inst))
            logging.error(unicode(inst))
        except Exception as inst:
            QMessageBox.critical(self, qApp.organizationName(), "Hubo un error al intentar mostrar su reporte")
            logging.critical(unicode(inst))

    def printDocument(self):
        """
        Imprime el documento actual
        """
        try:

            base = reports.Reports.url

            if base == "":
                raise UserWarning(u"No existe una configuración para el " + "servidor de reportes")

            self.printer.setOrientation(self.orientation)
            self.printer.setPageSize(self.pageSize)

            web = base + self.web + self.printIdentifier + "&uname=" + self.user.user + "&hash=" + self.user.hash
            self.loaded = False

            self.webview.load(QUrl(web))

            self.webview.loadFinished[bool].connect(self.on_webview_loadFinished)
            self.webview.loadProgress[int].connect(self.on_webview_loadProgress)
        except NotImplementedError as inst:
            logging.error(unicode(inst))
            QMessageBox.information(
                self, qApp.organizationName(), u"La función de impresión no se ha " + "implementado para este modulo"
            )
        except UserWarning as inst:
            logging.error(unicode(inst))
            QMessageBox.critical(self, qApp.organizationName(), unicode(inst))
        except Exception as inst:
            logging.critical(unicode(inst))
            QMessageBox.critical(self, qApp.organizationName(), "Hubo un problema al intentar imprimir" + " su reporte")

    def on_webview_loadProgress(self, progress):
        """
        Muestra el progreso de la carga del reporte en un progressBar
        """
        self.printProgressBar.setValue(progress)

    def on_webview_loadFinished(self, status):
        if self.printProgressBar.isVisible():
            self.printProgressBar.hide()
        if not status:
            QMessageBox.critical(self, qApp.organizationName(), "El reporte no se pudo cargar")
            logging.error("No se pudo cargar el reporte")

        self.loaded = True

        printdialog = QPrintDialog(self.printer, self)
        if printdialog.exec_() == QDialog.Accepted:
            self.webview.print_(self.printer)

        del self.webview

    def deleteRow(self):
        """
        Funcion usada para borrar lineas de la tabla
        """
        index = self.tabledetails.currentIndex()

        if not index.isValid():
            return
        row = index.row()

        self.editmodel.removeRows(row, 1)
        self.updateLabels()

    def updateLabels(self):
        """
        Este metodo se llama para actualizar las etiquetas de totales en el
        formulario
        """
        raise NotImplementedError()

    def createActions(self):
        """
        Crea las acciones predefinidas del sistema
        """
        self.actionNew = self.createAction(
            text="Nuevo",
            tip="Crear un nuevo documento",
            icon=QIcon.fromTheme("document-new", QIcon(":/icons/res/document-new.png")),
            shortcut="Ctrl+n",
            slot=self.newDocument,
        )
        self.actionPreview = self.createAction(
            text="Previsualizar",
            tip=u"Vista de impresión del documento",
            icon=QIcon.fromTheme("document-preview", QIcon(":/icons/res/document-preview.png")),
            shortcut="Ctrl+p",
            slot=self.preview,
        )
        self.actionPrint = self.createAction(
            text="Imprimir",
            tip="Imprimir el documento",
            icon=QIcon.fromTheme("document-print", QIcon(":/icons/res/document-print.png")),
            slot=self.printDocument,
        )
        self.actionSave = self.createAction(
            text="Guardar",
            tip="Guardar el documento",
            icon=QIcon.fromTheme("document-save", QIcon(":/icons/res/document-save.png")),
            shortcut="Ctrl+g",
            slot=self.save,
        )
        self.actionCancel = self.createAction(
            text="Cancelar",
            tip=u"Cancelar la creación del nuevo documento",
            icon=QIcon.fromTheme("dialog-cancel", QIcon(":/icons/res/dialog-cancel.png")),
            shortcut="Esc",
            slot=self.cancel,
        )

        # edicion, TODO: QUE FUNCIONEN ESTAS ACCIONES
        self.actionCopy = self.createAction(
            text="Copiar", icon=QIcon.fromTheme("edit-copy", QIcon(":/icons/res/edit-copy.png")), shortcut="Ctrl+c"
        )
        self.actionCut = self.createAction(
            text="Cortar", icon=QIcon.fromTheme("edit-cut", QIcon(":/icons/res/edit-cut.png")), shortcut="Ctrl+x"
        )
        self.actionPaste = self.createAction(text="Pegar", icon=":/icons/res/edit-paste.png", shortcut="Ctrl+v")

        # navegación
        self.actionGoFirst = self.createAction(
            text="Primer documento",
            tip="Ir al primer documento",
            icon=QIcon.fromTheme("go-first", QIcon(":/icons/res/go-first.png")),
            slot=functools.partial(self.navigate, "first"),
        )
        self.actionGoPrevious = self.createAction(
            text="Documento anterior",
            tip="Ir al documento anterior",
            icon=QIcon.fromTheme("go-previous", QIcon(":/icons/res/go-previous.png")),
            slot=functools.partial(self.navigate, "previous"),
        )
        self.actionGoLast = self.createAction(
            text="Ultimo documento",
            tip="Ir al ultimo documento",
            icon=QIcon.fromTheme("go-last", QIcon(":/icons/res/go-last.png")),
            slot=functools.partial(self.navigate, "last"),
        )
        self.actionGoNext = self.createAction(
            text="Documento siguiente",
            tip="Ir al siguiente documento",
            icon=QIcon.fromTheme("go-next", QIcon(":/icons/res/go-next.png")),
            slot=functools.partial(self.navigate, "next"),
        )

        self.actionDeleteRow = self.createAction(
            text="Borrar la fila",
            icon=QIcon.fromTheme("edit-delete", QIcon(":/icons/res/edit-delete.png")),
            slot=self.deleteRow,
        )

        self.addActionsToToolBar()

    def addActionsToToolBar(self):
        """
        Añade las acciones predefinidas a la barra de tareas
        """
        self.toolBar.addActions(
            [self.actionNew, self.actionPreview, self.actionPrint, self.actionSave, self.actionCancel]
        )
        self.toolBar.addSeparator()

        self.toolBar.addActions(
            [self.actionGoFirst, self.actionGoPrevious, self.actionGoLast, self.actionGoNext, self.actionGoLast]
        )

    @pyqtSlot()
    @if_edit_model
    def on_txtObservations_textChanged(self):
        """
        Asignar las observaciones al editmodel
        """
        self.editmodel.observations = self.txtObservations.toPlainText().strip()
class MainWindow(KXmlGuiWindow):

    "Class which displays the main Danbooru Client window."

    def __init__(self,  *args):

        "Initialize a new main window."

        super(MainWindow,  self).__init__(*args)
        self.cache = KPixmapCache("danbooru")
        self.preferences = preferences.Preferences()
        self.api = None
        self.__ratings = None
        self.__step = 0

        self.url_list = self.preferences.boards_list
        self.max_retrieve = self.preferences.thumbnail_no

        self.statusbar = self.statusBar()
        self.progress = QProgressBar()
        self.thumbnailarea = None
        self.tag_dock = None
        self.pool_dock = None
        self.first_fetch_widget = None

        self.progress.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        # FIXME: Hackish, but how to make it small otherwise?
        self.progress.setMinimumSize(100, 1)
        self.statusbar.addPermanentWidget(self.progress)
        self.progress.hide()

        self.setup_welcome_widget()
        self.setup_actions()

    def reload_config(self):

        """Reload configuration after a change"""

        urls = self.preferences.boards_list

        if self.first_fetch_widget is not None:
            self.first_fetch_widget.setup_urls(urls)

        if self.thumbnailarea is not None:

            max_thumbnail = self.preferences.thumbnail_no
            max_rating = self.preferences.max_allowed_rating

            self.thumbnailarea.fetchwidget.limit = max_thumbnail
            self.thumbnailarea.fetchwidget.rating = max_rating
            self.thumbnailarea.fetchwidget.update_values()

            self.thumbnailarea.connectwidget.setup_urls(urls)

        self.url_list = self.preferences.boards_list
        self.max_retrieve = self.preferences.thumbnail_no




    def setup_welcome_widget(self):

        """Load the welcome widget at startup."""

        widget = QWidget()
        layout = QVBoxLayout()

        welcome = QLabel(parent=self)
        pix = QPixmap(KStandardDirs.locate("appdata","logo.png"))

        welcome.setPixmap(pix)
        welcome.setAlignment(Qt.AlignCenter)

        self.first_fetch_widget = connectwidget.ConnectWidget(
        self.preferences.boards_list, self)

        self.statusbar.addPermanentWidget(self.first_fetch_widget, 300)

        self.first_fetch_widget.connectionEstablished.connect(
            self.handle_connection)
        self.first_fetch_widget.rejected.connect(
            self.first_fetch_widget.hide)

        self.first_fetch_widget.hide()

        self.first_fetch_widget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)

        layout.addWidget(self.first_fetch_widget)
        layout.addWidget(welcome)
        widget.setLayout(layout)

        self.setCentralWidget(widget)

    def setup_tooltips(self):

        """Set tooltips for the actions."""

        self.connect_action.setToolTip(i18n("Connect to a Danbooru board"))
        self.fetch_action.setToolTip(
            i18n("Fetch thumbnails from a Danbooru board")
        )
        self.batch_download_action.setToolTip(i18n("Batch download images"))

    def create_actions(self):

        """Create actions for the main window."""

        self.connect_action = KAction(KIcon("document-open-remote"),
                                 i18n("Connect"), self)
        self.fetch_action = KAction(KIcon("download"), i18n("Download"),
                                    self)
        self.clean_action = KAction(KIcon("trash-empty"),
                               i18n("Clear thumbnail cache"),
                               self)
        self.batch_download_action = KAction(KIcon("download"),
                                             i18n("Batch download"), self)
        self.pool_toggle_action = KToggleAction(KIcon("image-x-generic"),
                                            i18n("Pools"), self)
        self.tag_display_action = KDualAction(i18n("Show tags"),
                                              i18n("Hide tags"),
                                              self)
        self.tag_display_action.setIconForStates(KIcon("image-x-generic"))
        self.tag_display_action.setEnabled(False)

        # Shortcuts
        connect_default = KAction.ShortcutTypes(KAction.DefaultShortcut)
        connect_active = KAction.ShortcutTypes(KAction.ActiveShortcut)

        self.connect_action.setShortcut(KStandardShortcut.open())
        self.fetch_action.setShortcut(KStandardShortcut.find())

        self.fetch_action.setEnabled(False)
        self.batch_download_action.setEnabled(False)
        self.pool_toggle_action.setEnabled(False)

    def setup_action_collection(self):

        """Set up the action collection by adding the actions."""

        action_collection = self.actionCollection()

        # Addition to the action collection
        action_collection.addAction("connect", self.connect_action)
        action_collection.addAction("fetch", self.fetch_action)
        action_collection.addAction("clean", self.clean_action)
        action_collection.addAction("batchDownload", self.batch_download_action)
        action_collection.addAction("poolDownload", self.pool_toggle_action)
        action_collection.addAction("tagDisplay", self.tag_display_action)

        KStandardAction.quit (self.close, action_collection)
        KStandardAction.preferences(self.show_preferences,
                                    action_collection)

        action_collection.removeAction(
            action_collection.action("help_contents"))
        action_collection.actionHovered.connect(self.setup_action_tooltip)

    def setup_actions(self):

        """Set up the relevant actions, tooltips, and load the RC file."""

        self.create_actions()
        self.setup_tooltips()
        self.setup_action_collection()

        # Connect signals
        self.connect_action.triggered.connect(self.connect)
        self.fetch_action.triggered.connect(self.get_posts)
        self.clean_action.triggered.connect(self.clean_cache)
        self.batch_download_action.triggered.connect(self.batch_download)
        self.pool_toggle_action.toggled.connect(self.pool_toggle)
        self.tag_display_action.activeChanged.connect(self.tag_display)

        window_options = self.StandardWindowOption(self.ToolBar| self.Keys |
                                                   self.Create | self.Save |
                                                   self.StatusBar)

        setupGUI_args = [
            QSize(500, 400), self.StandardWindowOption(window_options)
        ]

        #Check first in standard locations for danbooruui.rc

        rc_file = KStandardDirs.locate("appdata", "danbooruui.rc")

        if rc_file.isEmpty():
            setupGUI_args.append(os.path.join(sys.path [0],
                                              "danbooruui.rc"))
        else:
            setupGUI_args.append(rc_file)

        self.setupGUI(*setupGUI_args)

    def setup_action_tooltip(self, action):

        "Show statusbar help when actions are hovered."

        if action.isEnabled():
            self.statusBar().showMessage(action.toolTip(), 2000)

    def setup_connections(self):

        """Set up connections for post and tag retrieval."""

        if self.api is None:
            return

        self.api.postRetrieved.connect(self.update_progress)
        self.api.postDownloadFinished.connect(self.download_finished)
        self.api.tagRetrieved.connect(self.tag_dock.widget().add_tags)
        self.tag_dock.widget().itemDoubleClicked.connect(
            self.fetch_tagged_items)

    def show_preferences(self):

        "Show the preferences dialog."

        if KConfigDialog.showDialog("Preferences dialog"):
            return
        else:
            dialog = preferences.PreferencesDialog(self, "Preferences dialog",
                                                   self.preferences)
            dialog.show()
            dialog.settingsChanged.connect(self.reload_config)

    def connect(self, ok):

        "Connect to a Danbooru board."

        if self.thumbnailarea is None:
            self.first_fetch_widget.show()
        else:
            self.thumbnailarea.connectwidget.show()


    def restore(self):

        self.statusbar.removeWidget(self.connect_widget)

    def handle_connection(self, connection):

        self.api = None
        self.api = connection
        self.api.cache = self.cache

        if self.pool_dock is not None:
            self.pool_dock.hide()
            self.pool_dock.widget().clear()
            self.pool_toggle_action.setChecked(False)

        if self.thumbnailarea is not None:
            #TODO: Investigate usability
            self.clear(clear_pool=True)
            self.thumbnailarea.clear()
            self.thumbnailarea.api_data = self.api
            self.setup_connections()

        else:
            self.first_fetch_widget.connectionEstablished.disconnect()
            self.first_fetch_widget.rejected.disconnect()
            self.statusbar.removeWidget(self.first_fetch_widget)
            self.setup_area()

        self.api.cache = self.cache

        self.statusBar().showMessage(i18n("Connected to %s" % self.api.url),
                                     3000)
        self.fetch_action.setEnabled(True)

        # Set up pool widget

        pool_widget = poolwidget.DanbooruPoolWidget(self.api)
        self.pool_dock = QDockWidget("Pools", self)
        self.pool_dock.setObjectName("PoolDock")
        self.pool_dock.setAllowedAreas(Qt.BottomDockWidgetArea)
        self.pool_dock.setWidget(pool_widget)
        #self.pool_dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(Qt.BottomDockWidgetArea, self.pool_dock)
        self.pool_dock.widget().poolDownloadRequested.connect(
            self.pool_prepare)
        self.pool_dock.hide()

        self.pool_toggle_action.setEnabled(True)
        self.clear() # Needed to show properly the stuff after connecting

        self.api.get_post_list(tags="", limit=self.thumbnailarea.post_limit,
                               rating=self.preferences.max_allowed_rating,
                               blacklist=list(self.preferences.tag_blacklist))
        self.api.get_tag_list(name="",blacklist=list(self.preferences.tag_blacklist),
                              limit=20)

    def get_posts(self, ok):

        "Get posts from the connected Danbooru board."

        if not self.api:
            return

        self.thumbnailarea.fetchwidget.show()



    def handle_fetching(self, tags, max_rating, limit):

        """Slot connected to the dataSent signal of the fetch widget.

        The widgets are set up if they don't exist, and the API is queried
        to do the actual downloading of tags and

        """

        self.clear()

        self.thumbnailarea.fetchwidget.hide()

        if self.tag_dock is not None:
            self.tag_dock.widget().clear()

        self.thumbnailarea.post_limit = limit
        blacklist= list(self.preferences.tag_blacklist)
        self.api.get_post_list(tags=tags, limit=limit,
                               rating=max_rating,
                               blacklist=blacklist)

        tags = [item for item in tags if item]

        if not tags:
            # No related tags, fetch the most recent 20
            tags = ""
            self.api.get_tag_list(name=tags,blacklist=blacklist,
                                  limit=20)
        else:
            self.api.get_related_tags(tags=tags, blacklist=blacklist)

    def fetch_tagged_items(self, item):

        """Fetch items found in the tag list widget."""

        tag_name = unicode(item.text())
        self.clear()

        blacklist = self.preferences.tag_blacklist
        limit = self.preferences.thumbnail_no
        rating = self.preferences.max_allowed_rating

        self.api.get_post_list(page=1, tags=[tag_name],
                                blacklist=blacklist,
                                limit=limit,
                                rating=rating)
        self.api.get_related_tags(tags=[tag_name], blacklist=blacklist)

    def pool_toggle(self, checked):

        "Toggle the presence/absence of the pool dock."

        if not self.api:
            return

        if not checked:
            self.pool_dock.hide()
        else:
            self.pool_dock.show()

    def pool_prepare(self, pool_id):

        """Prepare the central area for pool image loading."""

        if self.thumbnailarea is None:
            self.setup_area()
        else:
            self.clear(clear_pool=False)

        self.api.get_pool(pool_id, blacklist=self.preferences.tag_blacklist,
                          rating=self.preferences.max_allowed_rating)

    def batch_download(self, ok):

        "Download images in batch."

        selected_items = self.thumbnailarea.selected_images()

        if not selected_items:
            return

        start_url = KUrl("kfiledialog:///danbooru")
        caption = i18n("Select a directory to save the images to")
        directory = KFileDialog.getExistingDirectoryUrl(start_url, self,
                                                        caption)

        if directory.isEmpty():
            return

        for item in selected_items:

            file_url = item.url_label.url()
            tags = item.data.tags

            # Make a local copy to append paths as addPath works in-place
            destination = KUrl(directory)

            file_name = KUrl(file_url).fileName()
            destination.addPath(file_name)

            job = KIO.file_copy(KUrl(file_url), destination, -1)
            job.setProperty("tags", QVariant(tags))
            job.result.connect(self.batch_download_slot)

    def setup_area(self):

        "Set up the central widget to display thumbnails."

        self.thumbnailarea = thumbnailarea.DanbooruTabWidget(self.api,
            self.preferences, self.preferences.thumbnail_no, self)

        self.setCentralWidget(self.thumbnailarea)

        self.thumbnailarea.connectwidget.connectionEstablished.connect(
                    self.handle_connection, type=Qt.UniqueConnection)
        self.thumbnailarea.connectwidget.rejected.connect(
            self.thumbnailarea.connectwidget.hide, type=Qt.UniqueConnection)

        self.thumbnailarea.fetchwidget.dataSent.connect(
            self.handle_fetching, type=Qt.UniqueConnection)
        self.thumbnailarea.fetchwidget.rejected.connect(
            self.thumbnailarea.fetchwidget.hide, type=Qt.UniqueConnection)

        # Set up tag widget

        blacklist = self.preferences.tag_blacklist
        tag_widget = tagwidget.DanbooruTagWidget(blacklist, self)
        self.tag_display_action.setActive(True)
        self.tag_display_action.setEnabled(True)
        self.tag_dock = QDockWidget("Similar tags", self)
        self.tag_dock.setObjectName("TagDock")
        self.tag_dock.setAllowedAreas(Qt.RightDockWidgetArea)
        self.tag_dock.setWidget(tag_widget)
        #self.tag_dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(Qt.RightDockWidgetArea, self.tag_dock)
        self.tag_dock.hide()

        # Container signal-slot connections

        self.setup_connections()

    def download_finished(self):

        """Slot called when all the data has been completed. Clears the progress
        bar and resets it to 0."""

        if not self.batch_download_action.isEnabled():
            self.batch_download_action.setEnabled(True)

        self.__step = 0
        self.progress.hide()

    def update_progress(self):

        "Update the progress bar."

        if not self.progress.isVisible():
            self.progress.show()

        self.__step += 1
        self.progress.setValue(self.__step)

    def clear(self, clear_pool=True):

        "Clear the central widget."

        if self.thumbnailarea is None:
            return

        self.thumbnailarea.clear()
        self.tag_dock.widget().clear()
        if clear_pool:
            self.pool_dock.widget().clear()
        self.batch_download_action.setEnabled(False)

    def clean_cache(self):

        "Purge the thumbnail cache."

        self.cache.discard()
        self.statusBar().showMessage(i18n("Thumbnail cache cleared."))

    def batch_download_slot(self, job):

        """Slot called when doing batch download, for each file retrieved.

        If Nepomuk tagging is enabled, each file is tagged using the item's
        respective tags.

        """

        if job.error():
            job.ui().showErrorMessage()
        else:
            if self.preferences.nepomuk_enabled:
                tags = job.property("tags").toPyObject()
                #danbooru2nepomuk.tag_danbooru_item(job.destUrl().path(),
                #                                   tags)

    def tag_display(self, state):

        """Display or hide the tag dock."""

        if self.tag_dock is None:
            self.tag_display_action.setActive(False)
            return

        if state:
            self.tag_dock.show()
        else:
            self.tag_dock.hide()