Пример #1
0
class Completer(QGraphicsProxyWidget, object):
    ''' Class for handling text autocompletion in the SDL scene '''
    def __init__(self, parent):
        ''' Create an autocompletion list popup '''
        widget = QListWidget()
        super(Completer, self).__init__(parent)
        self.setWidget(widget)
        self.string_list = QStringListModel()
        self._completer = QCompleter()
        self.parent = parent
        self._completer.setCaseSensitivity(Qt.CaseInsensitive)
        # For some reason the default minimum size is (61,61)
        # Set it to 0 so that the size of the box is not taken
        # into account when it is hidden.
        self.setMinimumSize(0, 0)
        self.prepareGeometryChange()
        self.resize(0, 0)
        self.hide()

    def set_completer_list(self):
        ''' Set list of items for the autocompleter popup '''
        compl = [item.replace('-', '_') for item in
                 self.parent.parentItem().completion_list]
        self.string_list.setStringList(compl)
        self._completer.setModel(self.string_list)

    def set_completion_prefix(self, completion_prefix):
        '''
            Set the current completion prefix (user-entered text)
            and set the corresponding list of words in the popup widget
        '''
        self._completer.setCompletionPrefix(completion_prefix)
        self.widget().clear()
        count = self._completer.completionCount()
        for i in xrange(count):
            self._completer.setCurrentRow(i)
            self.widget().addItem(self._completer.currentCompletion())
        self.prepareGeometryChange()
        if count:
            self.resize(self.widget().sizeHintForColumn(0) + 40, 70)
        else:
            self.resize(0, 0)
        return count

    # pylint: disable=C0103
    def keyPressEvent(self, e):
        super(Completer, self).keyPressEvent(e)
        if e.key() == Qt.Key_Escape:
            self.parentItem().setFocus()
        # Consume the event so that it is not repeated at EditableText level
        e.accept()

    # pylint: disable=C0103
    def focusOutEvent(self, event):
        ''' When the user leaves the popup, return focus to parent '''
        super(Completer, self).focusOutEvent(event)
        self.hide()
        self.resize(0, 0)
        self.parentItem().setFocus()
Пример #2
0
class PestaniaProducto():
    def __init__(self, winPrincipal):
        self.winPrincipal = winPrincipal
        self.producto = Producto()
        self.proveedor = Proveedor()
        self.marca = Marca()
        self.rubro = Rubro()
        self.estado = ""
        self.conexionProducto = conexionProducto()

        self.completerRubro = QCompleter()
        self.completerMarca = QCompleter()
        self.completerProveedor = QCompleter()
        self.configInit()

    def configInit(self):
        #Configurando botones
        self.winPrincipal.btnAgregar_p.clicked.connect(self.onClickAgregar_p)
        self.winPrincipal.btnGuardar_p.clicked.connect(self.onClickGuardar_p)
        self.winPrincipal.btnBorrar_p.clicked.connect(self.onClickBorrar_p)
        self.winPrincipal.btnModificar_p.clicked.connect(
            self.onClickModificar_p)

        self.winPrincipal.btnRubro_p.clicked.connect(windowRubro)
        self.winPrincipal.btnMarca_p.clicked.connect(windowMarca)

        #windowMarca.connect(windowMarca, Qt.SIGNAL('destroyed()'), self.onQuitMarca)
        self.winPrincipal.txtFilterProductos_p.returnPressed.connect(
            self.search)

        #self.cargarTabla()
        self.winPrincipal.tvProductos_p.setMouseTracking(True)
        self.winPrincipal.tvProductos_p.setSelectionBehavior(
            QAbstractItemView.SelectRows)

        self.setCompleterMarca()
        self.setCompleterRubro()
        self.setCompleterProveedor()

        self.winPrincipal.txtFilterProductos_p.setFocus(True)

    def finish(self):
        self.winPrincipal.btnAgregar_p.disconnect()
        self.winPrincipal.btnGuardar_p.disconnect()
        self.winPrincipal.btnModificar_p.disconnect()
        self.winPrincipal.btnBorrar_p.disconnect()
        self.winPrincipal.btnRubro_p.disconnect()
        self.winPrincipal.btnMarca_p.disconnect()
        self.winPrincipal.tvProductos_p.disconnect()

    def search(self):
        if self.winPrincipal.txtFilterProductos_p.hasFocus() is True:
            self.cargarTabla()

    def onClickAgregar_p(self):
        self.estado = 'AGREGAR'
        self.validarBotones(button='AGREGAR')

    def onClickGuardar_p(self):
        validar = self.validar()

        if validar != "":
            print(validar)
            alert = QDialog()
            QMessageBox.information(alert, "ERROR", validar)
        else:
            self.producto.setDescripcion(
                str(self.winPrincipal.txtDescripcion_p.toPlainText()))
            self.producto.setCantidad(
                int(self.winPrincipal.sbCantidad_p.value()))
            self.producto.setCantidadMinima(
                int(self.winPrincipal.sbCantidadMin_p.value()))
            if self.winPrincipal.cbEstado_p.currentText() == "ACTIVO":
                self.producto.setEstado(1)
            else:
                self.producto.setEstado(0)

            if self.winPrincipal.rbFemenino_p.isChecked() is True:
                self.producto.setGenero("F")
            elif self.winPrincipal.rbMasculino_p.isChecked() is True:
                self.producto.setGenero("M")
            else:
                self.producto.setGenero("I")

            self.producto.setNombre(str(self.winPrincipal.txtNombre_p.text()))
            self.producto.setPrecioCompra(
                float(self.winPrincipal.txtPrecioCompra_p.text()))
            self.producto.setPrecioVenta(
                float(self.winPrincipal.txtPrecioVenta_p.text()))

            self.rubro.setRubro(str(self.completerRubro.currentCompletion()))
            self.producto.setRubro(self.rubro)

            self.proveedor.setDescripcion(
                str(self.completerProveedor.currentCompletion()))
            self.producto.setProveedor(self.proveedor)

            self.marca.setMarca(str(self.completerMarca.currentCompletion()))
            self.producto.setMarca(self.marca)

            if self.estado == 'AGREGAR':
                self.insertarProducto()
            elif self.estado == 'MODIFICAR':
                self.modificarProducto()

            self.validarBotones('GUARDAR')

    def onClickModificar_p(self):
        self.estado = 'MODIFICAR'
        self.validarBotones(button='MODIFICAR')

    def onClickBorrar_p(self):
        if self.winPrincipal.btnGuardar_p.isEnabled() != True:
            self.conexionProducto.borrarProducto(self.producto)
            self.cargarTabla()

        self.validarBotones(button='BORRAR')

    def cargarTabla(self):

        parameter = self.winPrincipal.txtFilterProductos_p.text()
        typeParameter = ''
        if self.winPrincipal.cbFilterProducto_p.currentText() == 'Nombre':
            typeParameter = 'p.nombre'
        elif self.winPrincipal.cbFilterProducto_p.currentText() == 'Marca':
            typeParameter = 'm.descripcion'
        else:
            typeParameter = 'r.descripcion'

        parameterState = 1
        if self.winPrincipal.cbInactivo_p.isChecked() is True:
            parameterState = 0

        parameterStock = 1
        if self.winPrincipal.cbSinStock_p.isChecked() is True:
            parameterStock = 0

        listaProductos = self.conexionProducto.selectProducto(
            typeParameter, parameter, parameterState, parameterStock)

        if len(listaProductos) > 0:

            header = [
                'idPro', 'Nombre', 'Descripcion', 'PC', 'PV', 'G', 'Estado',
                'Cant', 'Cantidad Min', 'idMar', 'Marca', 'idRubro', 'Rubro',
                'idProv', 'Proveedor'
            ]
            self.tablaModel = MyTableModel(self.winPrincipal.tvProductos_p,
                                           listaProductos, header)
            self.winPrincipal.tvProductos_p.setModel(self.tablaModel)
            self.winPrincipal.tvProductos_p.selectionModel(
            ).currentChanged.connect(self.changeSelectedTable)

            self.winPrincipal.tvProductos_p.setColumnHidden(0, True)
            self.winPrincipal.tvProductos_p.setColumnWidth(1, 200)
            self.winPrincipal.tvProductos_p.setColumnWidth(2, 320)
            self.winPrincipal.tvProductos_p.setColumnWidth(3, 60)
            self.winPrincipal.tvProductos_p.setColumnWidth(4, 60)
            self.winPrincipal.tvProductos_p.setColumnWidth(5, 60)
            self.winPrincipal.tvProductos_p.setColumnHidden(6, True)
            self.winPrincipal.tvProductos_p.setColumnWidth(7, 40)
            self.winPrincipal.tvProductos_p.setColumnHidden(8, True)
            self.winPrincipal.tvProductos_p.setColumnHidden(9, True)
            self.winPrincipal.tvProductos_p.setColumnWidth(10, 130)
            self.winPrincipal.tvProductos_p.setColumnHidden(11, True)
            self.winPrincipal.tvProductos_p.setColumnWidth(12, 130)
            self.winPrincipal.tvProductos_p.setColumnHidden(13, True)
            self.winPrincipal.tvProductos_p.setColumnWidth(14, 130)
        else:
            self.winPrincipal.tvProductos_p.setModel(None)

    def changeSelectedTable(self, selected, deselected):

        productoList = selected.model().mylist
        productoSelected = productoList[selected.row()]
        self.producto = Producto()
        self.rubro = Rubro()
        self.proveedor = Proveedor()
        self.marca = Marca()

        self.producto.setIdProducto(productoSelected[0])
        self.producto.setNombre(productoSelected[1])
        self.producto.setDescripcion(productoSelected[2])
        self.producto.setPrecioCompra(productoSelected[3])
        self.producto.setPrecioVenta(productoSelected[4])
        self.producto.setGenero(productoSelected[5])
        self.producto.setEstado(productoSelected[6])
        self.producto.setCantidad(productoSelected[7])
        self.producto.setCantidadMinima(productoSelected[8])

        self.marca.setIdMarca(productoSelected[9])
        self.marca.setMarca(productoSelected[10])
        self.producto.setMarca(self.marca)

        self.rubro.setIdRubro(productoSelected[11])
        self.rubro.setRubro(productoSelected[12])
        self.producto.setRubro(self.rubro)

        self.proveedor.setIdProveedor(productoSelected[13])
        self.proveedor.setDescripcion(productoSelected[14])
        self.producto.setProveedor(self.proveedor)

        self.winPrincipal.tvProductos_p.setRowHeight(deselected.row(), 33)
        self.winPrincipal.tvProductos_p.setRowHeight(selected.row(), 45)

        self.setCampos()
        self.winPrincipal.btnModificar_p.setEnabled(True)
        self.winPrincipal.btnBorrar_p.setEnabled(True)

    def validarBotones(self, button):
        if button == 'AGREGAR':
            self.winPrincipal.wDatosProducto.setEnabled(True)
            self.winPrincipal.btnBorrar_p.setEnabled(True)
            self.winPrincipal.btnBorrar_p.setText('CANCELAR')
            self.winPrincipal.btnGuardar_p.setEnabled(True)
            self.winPrincipal.btnModificar_p.setEnabled(False)
            self.winPrincipal.btnAgregar_p.setEnabled(False)
            self.winPrincipal.tvProductos_p.setEnabled(False)
            self.limpiarCampos()

        elif button == 'GUARDAR':
            self.winPrincipal.btnModificar_p.setEnabled(False)
            self.winPrincipal.btnAgregar_p.setEnabled(True)
            self.winPrincipal.btnGuardar_p.setEnabled(False)
            self.winPrincipal.btnBorrar_p.setText('BORRAR')
            self.winPrincipal.btnBorrar_p.setEnabled(False)
            self.winPrincipal.tvProductos_p.setEnabled(True)
            self.winPrincipal.wDatosProducto.setEnabled(False)
            self.limpiarCampos()

        elif button == 'MODIFICAR':
            self.winPrincipal.btnModificar_p.setEnabled(False)
            self.winPrincipal.btnAgregar_p.setEnabled(False)
            self.winPrincipal.btnGuardar_p.setEnabled(True)
            self.winPrincipal.btnBorrar_p.setText('CANCELAR')
            self.winPrincipal.btnBorrar_p.setEnabled(True)
            self.winPrincipal.tvProductos_p.setEnabled(False)
            self.winPrincipal.wDatosProducto.setEnabled(True)

        elif button == 'BORRAR':
            self.winPrincipal.btnModificar_p.setEnabled(False)
            self.winPrincipal.btnAgregar_p.setEnabled(True)
            self.winPrincipal.btnGuardar_p.setEnabled(False)
            self.winPrincipal.btnBorrar_p.setText('BORRAR')
            self.winPrincipal.btnBorrar_p.setEnabled(False)
            self.winPrincipal.tvProductos_p.setEnabled(True)
            self.winPrincipal.wDatosProducto.setEnabled(False)
            self.limpiarCampos()

    def insertarProducto(self):
        self.conexionProducto.insertarProducto(producto=self.producto)
        self.cargarTabla()

    def modificarProducto(self):
        self.conexionProducto.modificarProducto(self.producto)
        self.cargarTabla()

    def limpiarCampos(self):
        self.winPrincipal.txtNombre_p.setText('')
        self.winPrincipal.txtPrecioCompra_p.setText('')
        self.winPrincipal.txtPrecioVenta_p.setText('')
        self.winPrincipal.sbCantidad_p.setValue(0)
        self.winPrincipal.sbCantidadMin_p.setValue(0)
        self.winPrincipal.txtProveedor_p.setText('')
        self.winPrincipal.txtDescripcion_p.setText('')
        self.winPrincipal.txtRubro_p.setText('')
        self.winPrincipal.txtMarca_p.setText('')
        self.completerProveedor.setCurrentRow(0)
        self.completerRubro.setCurrentRow(0)
        self.completerMarca.setCurrentRow(0)
        self.winPrincipal.txtFilterProductos_p.setText('')
        self.winPrincipal.tvProductos_p.setModel(None)

        self.winPrincipal.txtFilterProductos_p.setFocus(True)

    def setCampos(self):
        self.winPrincipal.txtNombre_p.setText(self.producto.getNombre())
        self.winPrincipal.txtDescripcion_p.setText(
            str(self.producto.getDescripcion()))
        self.winPrincipal.txtPrecioCompra_p.setText(
            str(self.producto.getPrecioCompra()))
        self.winPrincipal.txtPrecioVenta_p.setText(
            str(self.producto.getPrecioVenta()))
        self.winPrincipal.txtProveedor_p.setText(
            str(self.producto.getProveedor().getDescripcion()))

        self.winPrincipal.sbCantidad_p.setValue(
            int(self.producto.getCantidad()))
        self.winPrincipal.sbCantidadMin_p.setValue(
            int(self.producto.getCantidadMinima()))

        self.winPrincipal.txtRubro_p.setText(
            str(self.producto.getRubro().getRubro()))
        self.winPrincipal.txtMarca_p.setText(
            str(self.producto.getMarca().getMarca()))

        if self.producto.getEstado() == 1:
            self.winPrincipal.cbEstado_p.setCurrentIndex(0)
        else:
            self.winPrincipal.cbEstado_p.setCurrentIndex(1)

        if self.producto.getGenero() == 'F':
            self.winPrincipal.rbFemenino_p.setChecked(True)
        elif self.producto.getGenero() == 'M':
            self.winPrincipal.rbMasculino_p.setChecked(True)
        else:
            self.winPrincipal.rbIndiferente_p.setChecked(True)

    def validar(self):
        mensaje = ''
        if self.winPrincipal.txtNombre_p.text() == '':
            mensaje = "Falta ingresar Nombre"
        elif self.winPrincipal.txtPrecioCompra_p.text() == '':
            mensaje = "Falta ingresar Precio de Compra"
        elif self.winPrincipal.txtPrecioVenta_p.text() == '':
            mensaje = "Falta ingresar Precio de Venta"
        elif self.completerProveedor.currentCompletion() == '':
            mensaje = "Falta ingresar un Proveedor"
        elif self.completerMarca.currentCompletion() == '':
            mensaje = "Falta seleccionar la marca"
        elif self.completerRubro.currentCompletion() == '':
            mensaje = 'Falta seleccionar el rubro'
        """elif self.completerProveedor.currentCompletion() =='' or self.completerProveedor.currentRow() == 0:
            mensaje= "Falta ingresar un Proveedor"
        elif self.completerMarca.currentCompletion() == '' or self.completerMarca.currentRow() == 0:
            mensaje = "Falta seleccionar la marca"
        elif self.completerRubro.currentCompletion() == '' or self.completerRubro.currentRow() == 0:
            mensaje = 'Falta seleccionar el rubro'
        """
        return mensaje

    def setCompleterMarca(self):
        listMarcas = self.conexionProducto.listMarcas()
        self.completerMarca = QCompleter(listMarcas)

        self.completerMarca.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.winPrincipal.txtMarca_p.setCompleter(self.completerMarca)

    def setCompleterRubro(self):
        listRubros = self.conexionProducto.listRubro()
        self.completerRubro = QCompleter(listRubros)
        #self.completerRubro.dynamicPropertyNames()
        self.completerRubro.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.winPrincipal.txtRubro_p.setCompleter(self.completerRubro)

    def setCompleterProveedor(self):
        listProveedores = self.conexionProducto.listProveedor()
        self.completerProveedor = QCompleter(listProveedores)

        self.completerProveedor.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.winPrincipal.txtProveedor_p.setCompleter(self.completerProveedor)
Пример #3
0
class TextStatusEditComplete(TextStatusEdit):
    """ Adds Completion functions to the base class

    This class extends 'TextStatusEdit' by:

    1.  providing a QCompleter to validate lines for the
        'fixupText' and 'lineChanged' signals
    2.  providing a popup for suggested completions as
        the user is typing
    3.  auto-completing the line when the user selects
        a suggestion.

    The task of auto completion and providing suggestions
    is provided directly by this class.

    The task validating and cleaning up text is provided by
    the PluginFinder.
    """
    def __init__(self, parent: QWidget = None):
        super().__init__(parent)
        self._dataModel = None
        self._monitorDbChanges = False
        self._enableAutoCompletion = False
        self._completedAndSelected = False
        self._completer = QCompleter(self)

        self._completer.setWidget(self)
        self._completer.setWrapAround(False)
        self._completer.setCompletionMode(QCompleter.PopupCompletion)
        self._completer.setCaseSensitivity(Qt.CaseInsensitive)
        self._completer.setFilterMode(Qt.MatchStartsWith)
        self._completer.setModelSorting(
            QCompleter.CaseInsensitivelySortedModel)
        self._completer.activated.connect(self.replaceLine)

        self._pluginFinder = PluginFinder(self._completer, self)
        self.fixupText.connect(self._pluginFinder.fixupText)
        self.lineChanged.connect(self._pluginFinder.setRowForLine)

        QShortcut(Qt.CTRL + Qt.Key_E, self, self.toggleAutoCompletion)
        QShortcut(Qt.CTRL + Qt.Key_T, self, self.suggestCompletions)

    # --- Methods related to the completer's underlying data model

    def setModel(self, model: QAbstractItemModel):
        self._completer.setModel(model)

    def _updateModelSignals(self):
        """ We do not need to check for column changes due to
        the way our PluginModel is structured. """

        if self._dataModel is not None:
            self._dataModel.rowsMoved.disconnect(self.resetData)
            self._dataModel.rowsInserted.disconnect(self.resetData)
            self._dataModel.rowsRemoved.disconnect(self.resetData)
            self._dataModel.modelReset.disconnect(self.resetData)
            self._dataModel.dataChanged.disconnect(self.resetData)
            self._dataModel.layoutChanged.disconnect(self.resetData)

        if self._monitorDbChanges:
            self._dataModel = self._completer.model()
            if self._dataModel is not None:
                self._dataModel.rowsMoved.connect(self.resetData)
                self._dataModel.rowsInserted.connect(self.resetData)
                self._dataModel.rowsRemoved.connect(self.resetData)
                self._dataModel.modelReset.connect(self.resetData)
                self._dataModel.dataChanged.connect(self.resetData)
                self._dataModel.layoutChanged.connect(self.resetData)
        else:
            self._dataModel = None

    def monitorDbChanges(self, enable: bool):
        """ Enable invalidating line status when
        the data model changes.

        Depending on the underlying data model, it may
        be unnecessary to monitor these changes, or, a
        higher level class can monitor specific signals
        more efficiently.  So, this is not enabled
        by default.  """

        if self._monitorDbChanges == enable:
            return

        self._monitorDbChanges = enable
        if enable:
            self._dataModel = self._completer.model()
            self._completer.completionModel().sourceModelChanged.connect(
                self._updateModelSignals)
        else:
            self._completer.completionModel().sourceModelChanged.disconnect(
                self._updateModelSignals)
        self._updateModelSignals()

    # ---- Methods related to line completion

    def completer(self):
        return self._completer

    def enableAutoCompletion(self, enable: bool):
        self._enableAutoCompletion = enable

    def toggleAutoCompletion(self):
        self.enableAutoCompletion(not self._enableAutoCompletion)

    def _textUnderCursor(self):
        tc = self.textCursor()
        if tc.positionInBlock() == 0 and len(tc.block().text()) > 1:
            tc.movePosition(QTextCursor.NextCharacter)
        tc.movePosition(QTextCursor.StartOfLine, QTextCursor.KeepAnchor)
        return tc.selectedText().lstrip()

    def suggestCompletions(self):
        if self.isLineInvalid(self.textCursor().blockNumber()):
            self._suggestCompletionsForText(self._textUnderCursor())

    def _suggestCompletionsForText(self, prefix: str):
        if not prefix:
            return
        if prefix != self._completer.completionPrefix():
            self._completer.setCompletionPrefix(prefix)
            self._completer.popup().setCurrentIndex(
                self._completer.completionModel().index(0, 0))
        if self._completer.completionCount() == 1:
            self._insertSuggestion(self._completer.currentCompletion())
        else:
            rect = self.cursorRect()
            rect.moveRight(self.statusAreaWidth())
            rect.setWidth(
                self._completer.popup().sizeHintForColumn(
                    self._completer.completionColumn()) +
                self._completer.popup().verticalScrollBar().sizeHint().width())
            self._completer.complete(rect)

    def _insertSuggestion(self, text: str):
        """ Only one suggestion matched, prefill line """

        cursor = self.textCursor()
        # handle when cursor is in middle of line
        if not cursor.atBlockEnd():
            cursor.beginEditBlock()
            cursor.select(QTextCursor.LineUnderCursor)
            cursor.removeSelectedText()
            cursor.insertText(text)
            cursor.movePosition(QTextCursor.StartOfLine)
            cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
            self._completedAndSelected = True
            self.setTextCursor(cursor)
            cursor.endEditBlock()
            return

        # handle when cursor at end of line
        cursor.beginEditBlock()
        numCharsToComplete = len(text) - len(
            self._completer.completionPrefix())
        insertionPosition = cursor.position()
        cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
        cursor.removeSelectedText()
        cursor.insertText(text[-numCharsToComplete:])
        cursor.setPosition(insertionPosition)
        cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
        self._completedAndSelected = True
        self.setTextCursor(cursor)
        cursor.endEditBlock()

    def keyPressEvent(self, event: QKeyEvent):
        if self._completedAndSelected and self.handledCompletedAndSelected(
                event):
            return

        self._completedAndSelected = False
        if self._completer.popup().isVisible():
            ignoredKeys = [
                Qt.Key_Up,
                Qt.Key_Down,
                Qt.Key_Enter,
                Qt.Key_Return,
                Qt.Key_Tab,
                Qt.Key_Escape,
            ]
            if event.key() in ignoredKeys:
                event.ignore()
                return
            self._completer.popup().hide()

        super().keyPressEvent(event)
        if not self._enableAutoCompletion:
            return

        ctrlOrShift = (event.modifiers() & Qt.ShiftModifier == Qt.ShiftModifier
                       or event.modifiers() & Qt.ControlModifier
                       == Qt.ControlModifier)

        if ctrlOrShift and not event.text():
            return

        if self.textCursor().atBlockEnd():
            self.suggestCompletions()

    def mousePressEvent(self, event: QMouseEvent):
        if self._completedAndSelected:
            self._completedAndSelected = False
            self.document().undo()
        super().mousePressEvent(event)

    def handledCompletedAndSelected(self, event: QKeyEvent):
        """ The line is prefilled when only one completion matches. The user
        can accept the suggestion by pressing 'Enter'. The user can reject
        the suggestion by pressing 'Esc' or by continuing to type. """

        self._completedAndSelected = False
        cursor = self.textCursor()
        acceptKeys = [Qt.Key_Enter, Qt.Key_Return, Qt.Key_Tab]
        if event.key() in acceptKeys:
            self.replaceLine(self._completer.currentCompletion())
        elif event.key() == Qt.Key_Escape:
            self.document().undo()
        else:
            self.document().undo()
            return False

        self.setTextCursor(cursor)
        event.accept()
        return True

    def replaceLine(self, text: str):
        cursor = self.textCursor()
        cursor.beginEditBlock()
        cursor.select(QTextCursor.LineUnderCursor)
        cursor.removeSelectedText()
        cursor.insertText(text)
        cursor.movePosition(QTextCursor.EndOfLine)
        self.setTextCursor(cursor)
        cursor.endEditBlock()

    # ---- Methods related to Context Menu

    def createStandardContextMenu(self, pos: QPoint):
        menu = super().createStandardContextMenu(pos)
        menu.addSeparator()
        autoCompletionAction = menu.addAction(
            QIcon(),
            self.tr("Enable Auto Complete"),
            self.toggleAutoCompletion,
            QKeySequence(Qt.CTRL + Qt.Key_E),
        )
        autoCompletionAction.setCheckable(True)
        autoCompletionAction.setChecked(self._enableAutoCompletion)

        completionAction = menu.addAction(
            QIcon(),
            self.tr("Suggest Completions"),
            self.suggestCompletions,
            QKeySequence(Qt.CTRL + Qt.Key_T),
        )
        completionAction.setEnabled(
            self.isLineInvalid(self.textCursor().blockNumber()))
        return menu
Пример #4
0
class FilteringComboBox(Widget):
    """Combination of QCombobox and QLineEdit with autocompletionself.
    Line edit and completer model is taken from QSqlTable mod

    Parameters:
        table (str): db table name containing data for combobox
        column (str): column name containing data for combobox
        color (str): 'rgb(r, g, b)' used for primary color
        font_size (int): default text font size in pt
        _model (QSqlTableModel): data model
        _col (int): display data model source coulumn
        _proxy (QSortFilterProxyModel): completer data model.
                                        _proxy.sourceModel() == _model
        _le (QLineEdit): QCombobox LineEdit


    Methods:
        createEditor(): (Widget): returns user input widgets
        value(): (str): returns user input text value
        setValue(value(str)): sets editor widget display value
        style(): (str): Returns CSS stylesheet string for input widget
        updateModel(): updates input widget model

    Args:
        table (str): db table name containing data for combobox
        column (str): column name containing data for combobox
    """
    def __init__(self,
                 parent,
                 placeholderText,
                 table,
                 column,
                 color='rgb(0,145,234)',
                 image=''):
        self.table = table
        self.column = column
        self.color = color
        super().__init__(parent, placeholderText, image)
        self.updateModel()

    def createEditor(self):
        # setup data model
        self._model = QSqlTableModel()
        self._model.setTable(self.table)
        self._col = self._model.fieldIndex(self.column)

        # setup filter model for sorting and filtering
        self._proxy = QSortFilterProxyModel()
        self._proxy.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self._proxy.setSourceModel(self._model)
        self._proxy.setFilterKeyColumn(self._col)

        # setup completer
        self._completer = QCompleter()
        self._completer.setModel(self._proxy)
        self._completer.setCompletionColumn(self._col)
        self._completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)

        # setup combobox
        editor = QComboBox()
        editor.setModel(self._proxy)
        editor.setModelColumn(self._col)
        editor.setEditable(True)
        editor.setFocusPolicy(Qt.StrongFocus)
        editor.setInsertPolicy(QComboBox.NoInsert)
        editor.setCompleter(self._completer)

        # setup connections
        editor.currentTextChanged[str].connect(self.onActivated)

        # setup editor appearence
        style = self.style()
        editor.setStyleSheet(style)
        editor.lineEdit().setStyleSheet(style)
        font = editor.font()
        self._completer.popup().setFont(font)

        return editor

    @pyqtSlot(str)
    def onActivated(self, text):
        print('combo_box filter text', text)
        if not text:  # placeholder text displayed and label is not visible
            self._editor.setCurrentIndex(-1)
            if self.toggle:
                self._label.showLabel(False)
        else:  # selected text is displayed and label is visible
            # self._editor.showPopup()
            # self._editor.lineEdit().setFocus()
            print('current copmpletion string',
                  self._completer.currentCompletion())
            self._proxy.setFilterFixedString(text)
            if self.toggle:
                self._label.showLabel(True)

    def style(self):
        """Returns stylesheet for editors

        Returns:
            style (str)
        """
        style = """
            QLineEdit {{
                border: none;
                padding-bottom: 2px;
                border-bottom: 1px solid rgba(0,0,0,0.42);
                background-color: white;
                color: rgba(0,0,0,0.42);
                font-size: {font_size}pt;}}

            QLineEdit:editable {{
                padding-bottom: 2px;
                border-bottom: 1px rgba(0,0,0,0.42);
                color: rgba(0,0,0,0.42);}}

            QLineEdit:disabled {{
                border: none;
                padding-bottom: 2px;
                border-bottom: 1px rgba(0,0,0,0.42);
                color: rgba(0,0,0,0.38);}}

            QLineEdit:hover {{
                padding-bottom: 2px;
                border-bottom: 2px solid rgba(0,0,0,0.6);
                color: rgba(0,0,0,0.54);
                }}

            QLineEdit:focus {{
                padding-bottom: 2px;
                border-bottom: 2px solid {color};
                color: rgba(0,0,0,0.87);}}

            QLineEdit:pressed {{
                border-bottom: 2px {color};
                font: bold;
                color: rgba(0,0,0,0.87)}}

            QComboBox {{
                border: none;
                padding-bottom: 2px;
                font-size: {font_size}pt;
                }}

            QComboBox::down-arrow {{
                image: url('dropdown.png');
                background-color: white;
                border: 0px white;
                padding: 0px;
                margin: 0px;
                height:14px;
                width:14px;}}

            QComboBox::drop-down{{
                subcontrol-position: center right;
                border: 0px;
                margin: 0px;
            }}

            QComboBox QAbstractItemView {{
                font: {font_size};}}

        """.format(color=self.color,
                   font_size=self._editor_font_size,
                   dropdown=DROPDOWN_PNG)
        return style

    def updateModel(self):
        model = self._editor.model().sourceModel()
        col = self._editor.modelColumn()
        model.select()
        model.sort(col, Qt.AscendingOrder)

    def value(self):
        return self._editor.currentText()

    def setValue(self, value):
        self._editor.setCurrentText(value)
Пример #5
0
class ImageManager(QMainWindow, Ui_Manager):
    _signal_update_image_id = pyqtSignal(QModelIndex, int)

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

        self.__db_helper = DBHelper(self.db_error_handler)  # 数据库操作

        # 下拉列表设置
        self.__type_model = MyBaseListModel()
        self.comboBox_type.setModel(self.__type_model)
        self.__type_model.add_items(self.__db_helper.get_model_data_list('type'))
        self.comboBox_type.setCurrentIndex(0)

        self.__level_model = MyBaseListModel()
        self.comboBox_level.setModel(self.__level_model)
        levels = self.__db_helper.get_model_data_list('level')
        for i in range(len(levels)):
            level = levels[i]
            if level.id == 10:
                levels.remove(level)
                levels.insert(4, level)
        self.__level_model.add_items(levels)
        self.comboBox_level.setCurrentIndex(0)

        # 图片信息
        self.__image_model = ImageFileListModel(self)
        self.__image_model.delete_repeat = self.checkBox_delete_repeat.isChecked()
        self.__config = ConfigHelper(self)

        threading.Thread(target=self._load_default_images, daemon=True).start()
        self.lineEdit_sql_where.setText(self.__config.get_config_key('history', 'sqlWhere'))
        self.lineEdit_export_dir.setText(self.__config.get_config_key('history', 'lastExportDir'))

        self.listView.setModel(self.__image_model)

        # 关联事件
        self.listView.selectionModel().currentChanged.connect(self.__on_list_view_current_row_change)
        self.listView.set_key_press_delegate(self.key_press_delegate)
        self.listView.set_action_show_file_directory_delegate(self.open_file_directory)
        self.pushButton_classify.clicked.connect(self.__classify)
        self.pushButton_search.clicked.connect(self.__search)
        self.pushButton_clean.clicked.connect(self.__clean_not_exist_images)
        self.checkBox_delete_repeat.clicked.connect(self._on_check_box_delete_repeat_click)
        self.actionOpen.triggered.connect(self.__open_files)
        self.lineEdit_sql_where.returnPressed.connect(self.__search)
        self.pushButton_export_dir.clicked.connect(self.__choose_export_dir)
        self.pushButton_export.clicked.connect(self.__choose_export)
        self.lineEdit_desc.returnPressed.connect(self.__classify)
        self.lineEdit_tag.returnPressed.connect(self.__classify)
        self.lineEdit_role.returnPressed.connect(self.__classify)
        self.lineEdit_works.returnPressed.connect(self.__classify)
        self.lineEdit_series.returnPressed.connect(self.__classify)
        self.lineEdit_source.returnPressed.connect(self.__classify)
        self.lineEdit_uploader.returnPressed.connect(self.__classify)
        self.lineEdit_author.returnPressed.connect(self.__classify)
        self._signal_update_image_id.connect(self._update_image_id)

        # 设置 tab 切换顺序
        self.setTabOrder(self.lineEdit_desc, self.lineEdit_tag)
        self.setTabOrder(self.lineEdit_tag, self.lineEdit_path)
        self.setTabOrder(self.lineEdit_path, self.comboBox_type)
        self.setTabOrder(self.comboBox_type, self.comboBox_level)
        self.setTabOrder(self.comboBox_level, self.lineEdit_role)
        self.setTabOrder(self.lineEdit_role, self.lineEdit_works)
        self.setTabOrder(self.lineEdit_works, self.lineEdit_series)
        self.setTabOrder(self.lineEdit_series, self.lineEdit_source)
        self.setTabOrder(self.lineEdit_source, self.lineEdit_uploader)
        self.setTabOrder(self.lineEdit_uploader, self.lineEdit_author)
        self.setTabOrder(self.lineEdit_author, self.pushButton_classify)
        self.setTabOrder(self.pushButton_classify, self.pushButton_search)

        # 自动补全
        self.__completer_list = []
        self.__completer_filename = 'works.txt'
        if not os.path.exists(self.__completer_filename):
            f = open(self.__completer_filename, 'w', encoding='utf-8')
            f.close()
        with open(self.__completer_filename, 'r+', encoding='utf-8') as f:
            self.__completer_list = list(map(lambda x: x.replace("\n", "").replace("\r", ""), f.readlines()))
        self.completer = QCompleter(self.__completer_list)
        self.completer.setCompletionMode(QCompleter.InlineCompletion)
        self.completer.setFilterMode(Qt.MatchContains)
        self.lineEdit_works.setCompleter(self.completer)
        self.lineEdit_works.editingFinished.connect(self.__add_complete)

        # Image.MAX_IMAGE_PIXELS = 1882320000
        self.listView.setFocus()

        # 预加载图片
        threading.Thread(target=self.__preload, daemon=True).start()

    def __add_complete(self):
        """
        添加自动补全作品
        :return:
        """
        cur_completion = self.completer.currentCompletion()
        if cur_completion == "":
            self.__completer_list.append(self.lineEdit_works.text())
            self.completer = QCompleter(self.__completer_list)
            self.completer.setCompletionMode(QCompleter.InlineCompletion)
            self.completer.setFilterMode(Qt.MatchContains)
            self.lineEdit_works.setCompleter(self.completer)
            print(self.__completer_list)

    def _load_default_images(self):
        last_dir = self.__config.get_config_key('history', 'lastDir')
        if os.path.isdir(last_dir) and os.path.exists(last_dir):
            self.__image_model.add_path(last_dir)
            if self.__image_model.rowCount() > 0:
                self.listView.setCurrentIndex(self.__image_model.index(0, 0))

    def __open_files(self):
        """
        打开图片文件
        :return:
        """
        path_list = \
            QtWidgets.QFileDialog.getOpenFileNames(self, "选择文件", filter='图片(*.jpg *.png *.gif *.jpeg *.bmp)')[0]
        # 生成List使用的Model
        for path in path_list:
            tp_lists = path.split('/')
            item_data = ImageFile(
                name="%s/%s" % (tp_lists[-2], tp_lists[-1]),
                full_path=path
            )
            self.__image_model.addItem(item_data)

    def __choose_export_dir(self):
        """
        选择保存文件夹
        :return:
        """
        dir_path = QtWidgets.QFileDialog.getExistingDirectory(self, "选择保存的文件夹", "E:/图片")
        self.lineEdit_export_dir.setText(dir_path)

    def __show_image(self, index):
        """
        显示指定索引文件名对应的图片
        :param index: 文件索引
        :return:
        """
        path = self.__image_model.get_item(index).full_path
        start_time = time.time()
        status = f"[{index + 1}/{self.__image_model.rowCount()}] {path}"
        try:
            # 填充缩放
            pixmap, is_preload = self.__get_image(path)
            cur_time = time.time()
            status += f"\t是否预加载:{is_preload}\t图片读取:${round((cur_time - start_time) * 1000, 2)}ms"
            start_time = time.time()
            # 加载图片
            item = QtWidgets.QGraphicsPixmapItem(pixmap)
            scene = QtWidgets.QGraphicsScene()
            scene.addItem(item)
            self.graphicsView.setScene(scene)
            cur_time = time.time()
            status += f"\t图片加载:${round((cur_time - start_time) * 1000, 2)}ms"
        except Exception as e:
            print(e)
            QMessageBox.information(self, "提示", str(e), QMessageBox.Ok)
        self.__analysis_file_info(path)
        self.statusbar.showMessage(status)

    def __on_list_view_current_row_change(self, current: QModelIndex, previous: QModelIndex):
        """
        图片列表当前行变化事件
        :param current: 当前行索引
        :param previous:
        :return:
        """
        self.__show_image(current.row())

    def __analysis_file_info(self, path):
        info = self.__db_helper.search_by_file_path(path)
        if not info:
            # 分析图片信息
            self.lineEdit_path.setText(path)
            info = ImageHelper.analyze_image_info(path)
            self.lineEdit_size.setText(f"{info.size} MB")
            self.dateTimeEdit_file_create.setDateTime(info.create_time)
            self.lineEdit_desc.setText(info.desc)
            self.lineEdit_tag.setText(info.tags)
            if info.source:
                self.lineEdit_source.setText(info.source)
            if info.uploader:
                self.lineEdit_uploader.setText(info.uploader)
            if info.author:
                self.lineEdit_author.setText(info.author)
            return
        # 显示已有记录
        self.lineEdit_desc.setText(info.desc)
        self.lineEdit_tag.setText(info.tags)
        self.lineEdit_path.setText(info.path)
        self.lineEdit_works.setText(info.works)
        self.lineEdit_source.setText(info.source)
        self.lineEdit_role.setText(info.role)
        self.lineEdit_author.setText(info.author)
        self.lineEdit_series.setText(info.series)
        self.lineEdit_uploader.setText(info.uploader)
        self.lineEdit_size.setText(f"{info.size} MB")
        self.comboBox_type.setCurrentIndex(self.__type_model.get_index(info.type_id))
        self.comboBox_level.setCurrentIndex(self.__level_model.get_index(info.level_id))
        self.dateTimeEdit_file_create.setDateTime(info.file_create_time)
        self.dateTimeEdit_create.setDateTime(info.create_time)
        self.dateTimeEdit_update.setDateTime(info.update_time)

    def __classify(self):
        """
        分类图片
        :return:
        """
        select_rows = self.listView.selectionModel().selectedRows()
        select_rows = [x for x in select_rows]
        th = threading.Thread(target=self.__insert_or_update_db, args=(select_rows,), daemon=True)
        th.start()
        end_index = select_rows[-1]
        self.__select_index(self.__image_model.index(end_index.row() + 1, end_index.column()))

    def __insert_or_update_db(self, select_rows):
        index = self.comboBox_type.currentIndex()
        type_id = self.__type_model.get_item(index).id
        index = self.comboBox_level.currentIndex()
        level_id = self.__level_model.get_item(index).id
        desc = self.lineEdit_desc.text()
        author = self.lineEdit_author.text()
        tags = self.lineEdit_tag.text()
        works = self.lineEdit_works.text()
        role = self.lineEdit_role.text()
        source = self.lineEdit_source.text()
        series = self.lineEdit_series.text()
        uploader = self.lineEdit_uploader.text()
        for i in range(len(select_rows)):
            item = self.__image_model.get_item(select_rows[i].row())
            path = item.full_path
            image = MyImage(id=item.id, desc=desc, author=author, type_id=type_id, level_id=level_id, tags=tags,
                            works=works, role=role, source=source, width=self.lineEdit_width.text(),
                            height=self.lineEdit_height.text(), size=FileHelper.get_file_size_in_mb(path),
                            filename=item.name, path=path, md5=FileHelper.get_md5(path),
                            file_create_time=FileHelper.get_create_time_str(path), series=series, uploader=uploader)
            if image.id == 0:
                self.__db_helper.insert_image(image)
                image_id = self.__db_helper.get_id_by_path(path)
                self._signal_update_image_id.emit(select_rows[i], image_id)
                self.dateTimeEdit_create.setDateTime(datetime.datetime.now())
                self.dateTimeEdit_update.setDateTime(datetime.datetime.now())
                # message = f"{item.name} 创建完成!"
            else:
                # 批量更新时,保持原来的描述、作者、等级、标签、作品
                old_image = self.__image_model.get_database_item(image.id)
                if old_image and len(select_rows) > 1:
                    image.desc = old_image.desc
                    image.author = old_image.author
                    image.level_id = old_image.level_id
                    image.tags = old_image.tags
                    image.works = old_image.works
                self.__db_helper.update_image(image)
                self.dateTimeEdit_update.setDateTime(datetime.datetime.now())
                message = f"{item.name} 更新完成!"
                self.statusbar.showMessage(f"[{i + 1}/{len(select_rows)}] {message}")
        # end_index = select_rows[-1]

    def _update_image_id(self, index: QModelIndex, image_id: int):
        self.__image_model.set_image_id(index,image_id)

    def __select_index(self, index: QModelIndex):
        if 0 < index.row() < self.__image_model.rowCount():
            self.listView.setCurrentIndex(index)
            self.listView.setFocus()
        else:
            self.listView.clearFocus()
            self.listView.setFocus()

    def __del_select_rows(self):
        """
        删除选中行
        :return:
        """
        select_rows = self.listView.selectionModel().selectedRows()
        if len(select_rows) == 0:
            return
        first_index = select_rows[0]
        for i in range(len(select_rows)):
            index = select_rows[i]
            item = self.__image_model.get_item(index.row() - i)
            if item.id != 0:
                self.__db_helper.delete(item.id)
            os.remove(item.full_path)
            self.__image_model.delete_item(index.row() - i)
            self.statusbar.showMessage(f"[{i + 1}/{len(select_rows)}] {item.name} 删除成功!")

        if len(select_rows) > 1:
            self.listView.clearSelection()
        # 如果删除到了最后一行,则刷新上一个
        if first_index.row() >= self.__image_model.rowCount():
            if first_index.row() == 0:
                return
            else:
                self.listView.setCurrentIndex(self.listView.model().index(first_index.row() - 1, first_index.column()))
        else:
            if len(select_rows) > 1:
                self.listView.setCurrentIndex(first_index)
            else:
                self.__show_image(first_index.row())

    def __search(self):
        sql_where = self.lineEdit_sql_where.text()
        if not sql_where:
            sql_where = ""
            if len(self.lineEdit_desc.text()):
                sql_where += f" `desc` like '%{self.lineEdit_desc.text()}%'"
            if len(self.lineEdit_role.text()):
                sql_where += f" `role` like '%{self.lineEdit_role.text()}%'"
            if len(self.lineEdit_works.text()):
                sql_where += f" `works` like '%{self.lineEdit_works.text()}%'"
            if len(self.lineEdit_series.text()):
                sql_where += f" `series` like '%{self.lineEdit_series.text()}%'"
            if len(self.lineEdit_source.text()):
                sql_where += f" `source` like '%{self.lineEdit_source.text()}%'"
            if len(self.lineEdit_uploader.text()):
                sql_where += f" `uploader` like '%{self.lineEdit_uploader.text()}%'"
            if len(self.lineEdit_author.text()):
                sql_where += f" `author` like '%{self.lineEdit_author.text()}%'"
        image_sql_list, image_file_list = self.__db_helper.search_by_where(sql_where)
        if len(image_sql_list) > 0:
            self.__image_model.set_images(image_sql_list, image_file_list)
            self.listView.setFocus()
            self.listView.scrollToTop()

    def __choose_export(self):
        dir_path = self.lineEdit_export_dir.text()
        if not os.path.exists(dir_path):
            os.makedirs(dir_path)
        if not os.path.isdir(dir_path):
            return

        for i in range(self.__image_model.rowCount()):
            image = self.__image_model.get_item(i)
            if image.id:
                image_sql = self.__image_model.get_database_item(image.id)
                if not os.path.exists(image_sql.path):
                    continue

                try:
                    FileHelper.copyfile_without_override(image_sql.path, dir_path)
                except Exception as e:
                    print(e)
            else:
                FileHelper.copyfile_without_override(image.full_path, dir_path)

            self.statusbar.showMessage(f"[{i + 1}/{self.__image_model.rowCount()}] {image.name} 复制成功!")

    def _on_check_box_delete_repeat_click(self):
        print(f'是否删除重复:{self.checkBox_delete_repeat.isChecked()}')
        self.__image_model.delete_repeat = self.checkBox_delete_repeat.isChecked()

    # region 重写 Qt 控件方法
    def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
        # 键盘快捷键事件
        if event.key() == Qt.Key_R and QApplication.keyboardModifiers() == Qt.ControlModifier:
            self.__classify()
            self.listView.setFocus()
        if event.key() == Qt.Key_E and QApplication.keyboardModifiers() == Qt.ControlModifier:
            self.comboBox_level.setFocus()
        if event.key() == Qt.Key_W and QApplication.keyboardModifiers() == Qt.ControlModifier:
            self.lineEdit_works.setText("")
        # if event.key() == Qt.Key_Delete:
        #     self.__del_select_rows()

    def key_press_delegate(self, event: QtGui.QKeyEvent):
        level_index = None
        if event.key() == Qt.Key_1:
            level_index = 1
        if event.key() == Qt.Key_2:
            level_index = 2
        if event.key() == Qt.Key_3:
            level_index = 3
        if event.key() == Qt.Key_4:
            level_index = 4
        if event.key() == Qt.Key_5:
            level_index = 5
        if event.key() == Qt.Key_6:
            level_index = 6
        if event.key() == Qt.Key_7:
            level_index = 7
        if event.key() == Qt.Key_8:
            level_index = 8
        if event.key() == Qt.Key_9:
            level_index = 9

        if level_index and self.__level_model.rowCount() >= level_index:
            self.comboBox_level.setCurrentIndex(level_index - 1)
            return True

        if event.key() == Qt.Key_R:
            self.__classify()
            return True
        if event.key() == Qt.Key_E:
            self.lineEdit_role.setFocus()
            return True
        if event.key() == Qt.Key_C:
            self.lineEdit_works.setText("")
            return True
        if event.key() == Qt.Key_D:
            self.__del_select_rows()
            return True
        if event.key() == Qt.Key_W:
            current_index = self.listView.currentIndex()
            if current_index.row() > 0:
                self.listView.setCurrentIndex(self.__image_model.index(current_index.row() - 1, current_index.column()))
            return True
        if event.key() == Qt.Key_S:
            current_index = self.listView.currentIndex()
            if current_index.row() < self.__image_model.rowCount() - 1:
                self.listView.setCurrentIndex(self.__image_model.index(current_index.row() + 1, current_index.column()))
            return True
        return False

    def dragEnterEvent(self, e: QtGui.QDragEnterEvent) -> None:
        # 设置允许接收
        e.accept()

    def dropEvent(self, e: QtGui.QDropEvent) -> None:
        # 接收文件夹和文件以刷新图片列表
        urls = e.mimeData().urls()
        th = threading.Thread(target=self.__load_list_data, args=(urls,), daemon=True)
        th.start()

    def __load_list_data(self, urls):
        self.__image_model.clear()
        for url in urls:
            self.__image_model.add_path(url.toLocalFile())
        if self.__image_model.rowCount() > 0:
            self.listView.setCurrentIndex(self.__image_model.index(0, 0))
        if not os.path.isdir(urls[0].toLocalFile()):
            return
        self.__config.add_config_key('history', 'lastDir', urls[0].toLocalFile())

    def closeEvent(self, event: QtGui.QCloseEvent) -> None:
        self.__config.add_config_key('history', 'lastExportDir', self.lineEdit_export_dir.text())
        self.__config.add_config_key('history', 'sqlWhere', self.lineEdit_sql_where.text())
        # 关闭时保存自动填充作品列表的配置文件
        with open(self.__completer_filename, 'w+', encoding='utf-8') as f:
            f.writelines(list(map(lambda x: x + "\n", self.__completer_list)))

    # endregion

    # region 预加载图片
    __preload_count = 5
    __preload_image_queue = queue.Queue(__preload_count)
    __preload_image_size = queue.Queue(__preload_count)
    __preload_lock = threading.Lock()

    def __preload(self):
        while True:
            if self.__preload_image_queue.qsize() == 5:
                time.sleep(1)
                continue

            index = self.listView.currentIndex().row()
            preload_index = index + self.__preload_image_queue.qsize() + 1
            image_file = self.__image_model.get_item(preload_index)
            if not image_file:
                time.sleep(1)
                continue

            full_path = image_file.full_path
            try:
                pixmap, width, height = ImageHelper.get_image_from_file(full_path, self.graphicsView.width(),
                                                                        self.graphicsView.height())
                self.__preload_image_queue.put(PreloadImage(full_path, pixmap))
                self.__preload_image_size.put((width, height))
                print(f"预加载成功:{full_path}")
            except Exception as e:
                print(e)
                print(f"预加载失败:{full_path}")
                time.sleep(1)

    def __get_image(self, path):
        # 优先从队列中获取
        while self.__preload_image_queue.qsize() > 0:
            image = self.__preload_image_queue.get()
            size = self.__preload_image_size.get()
            if isinstance(image, PreloadImage) and image.full_path == path:
                print("从预载中读取")
                self.lineEdit_width.setText(str(size[0]))
                self.lineEdit_height.setText(str(size[1]))
                return image.pixmap, True
        print("从文件中读取")
        image, width, height = ImageHelper.get_image_from_file(path, self.graphicsView.width(),
                                                               self.graphicsView.height())
        self.lineEdit_width.setText(str(width))
        self.lineEdit_height.setText(str(height))
        return image, False

    # endregion

    def __clean_not_exist_images(self):
        """
        清理不存在的图片
        :return:
        """
        th = threading.Thread(
            target=ImageHelper.refresh_recode_info,
            args=(self.db_error_handler,self.show_status_message,),
            daemon=True
        )
        th.start()

    def show_status_message(self, message):
        self.statusbar.showMessage(message)

    def open_file_directory(self):
        """
        打开文件所在目录并选中文件
        :return:
        """
        select_rows = self.listView.selectionModel().selectedRows()
        if not len(select_rows):
            return
        file_path = self.__image_model.get_item(select_rows[0].row()).full_path
        FileHelper.open_file_directory(file_path)

    def db_error_handler(self, error_str):
        QMessageBox.information(self, "提示", error_str, QMessageBox.Ok)
class PestaniaProducto():

    def __init__(self, winPrincipal):
        self.winPrincipal = winPrincipal
        self.producto = Producto()
        self.proveedor = Proveedor()
        self.marca = Marca()
        self.rubro = Rubro()
        self.estado = ""
        self.conexionProducto = conexionProducto()

        self.completerRubro = QCompleter()
        self.completerMarca = QCompleter()
        self.completerProveedor = QCompleter()
        self.configInit()



    def configInit(self):
        #Configurando botones
        self.winPrincipal.btnAgregar_p.clicked.connect(self.onClickAgregar_p)
        self.winPrincipal.btnGuardar_p.clicked.connect(self.onClickGuardar_p)
        self.winPrincipal.btnBorrar_p.clicked.connect(self.onClickBorrar_p)
        self.winPrincipal.btnModificar_p.clicked.connect(self.onClickModificar_p)

        self.winPrincipal.btnRubro_p.clicked.connect(windowRubro)
        self.winPrincipal.btnMarca_p.clicked.connect(windowMarca)

        #windowMarca.connect(windowMarca, Qt.SIGNAL('destroyed()'), self.onQuitMarca)
        self.winPrincipal.txtFilterProductos_p.returnPressed.connect(self.search)

        #self.cargarTabla()
        self.winPrincipal.tvProductos_p.setMouseTracking(True)
        self.winPrincipal.tvProductos_p.setSelectionBehavior(QAbstractItemView.SelectRows)

        self.setCompleterMarca()
        self.setCompleterRubro()
        self.setCompleterProveedor()

        self.winPrincipal.txtFilterProductos_p.setFocus(True)



    def finish(self):
        self.winPrincipal.btnAgregar_p.disconnect()
        self.winPrincipal.btnGuardar_p.disconnect()
        self.winPrincipal.btnModificar_p.disconnect()
        self.winPrincipal.btnBorrar_p.disconnect()
        self.winPrincipal.btnRubro_p.disconnect()
        self.winPrincipal.btnMarca_p.disconnect()
        self.winPrincipal.tvProductos_p.disconnect()

    def search(self):
        if self.winPrincipal.txtFilterProductos_p.hasFocus() is True:
            self.cargarTabla()

    def onClickAgregar_p(self):
        self.estado = 'AGREGAR'
        self.validarBotones(button='AGREGAR')

    def onClickGuardar_p(self):
        validar = self.validar()

        if validar != "":
            print(validar)
            alert = QDialog()
            QMessageBox.information(alert,"ERROR", validar)
        else:
            self.producto.setDescripcion(str(self.winPrincipal.txtDescripcion_p.toPlainText()))
            self.producto.setCantidad(int(self.winPrincipal.sbCantidad_p.value()))
            self.producto.setCantidadMinima(int(self.winPrincipal.sbCantidadMin_p.value()))
            if self.winPrincipal.cbEstado_p.currentText() == "ACTIVO":
                self.producto.setEstado(1)
            else:
                self.producto.setEstado(0)

            if self.winPrincipal.rbFemenino_p.isChecked() is True:
                self.producto.setGenero("F")
            elif self.winPrincipal.rbMasculino_p.isChecked() is True:
                self.producto.setGenero("M")
            else:
                self.producto.setGenero("I")

            self.producto.setNombre(str(self.winPrincipal.txtNombre_p.text()))
            self.producto.setPrecioCompra(float(self.winPrincipal.txtPrecioCompra_p.text()))
            self.producto.setPrecioVenta(float(self.winPrincipal.txtPrecioVenta_p.text()))

            self.rubro.setRubro(str(self.completerRubro.currentCompletion()))
            self.producto.setRubro(self.rubro)

            self.proveedor.setDescripcion(str(self.completerProveedor.currentCompletion()))
            self.producto.setProveedor(self.proveedor)

            self.marca.setMarca(str(self.completerMarca.currentCompletion()))
            self.producto.setMarca(self.marca)

            if self.estado == 'AGREGAR':
                self.insertarProducto()
            elif self.estado == 'MODIFICAR':
                self.modificarProducto()

            self.validarBotones('GUARDAR')


    def onClickModificar_p(self):
        self.estado = 'MODIFICAR'
        self.validarBotones(button='MODIFICAR')

    def onClickBorrar_p(self):
        if self.winPrincipal.btnGuardar_p.isEnabled() != True:
            self.conexionProducto.borrarProducto(self.producto)
            self.cargarTabla()

        self.validarBotones(button='BORRAR')

    def cargarTabla(self):

        parameter = self.winPrincipal.txtFilterProductos_p.text()
        typeParameter = ''
        if self.winPrincipal.cbFilterProducto_p.currentText() == 'Nombre':
            typeParameter = 'p.nombre'
        elif self.winPrincipal.cbFilterProducto_p.currentText() == 'Marca':
            typeParameter = 'm.descripcion'
        else:
            typeParameter = 'r.descripcion'

        parameterState = 1
        if self.winPrincipal.cbInactivo_p.isChecked() is True:
            parameterState = 0

        parameterStock = 1
        if self.winPrincipal.cbSinStock_p.isChecked() is True:
            parameterStock = 0

        listaProductos = self.conexionProducto.selectProducto(typeParameter, parameter, parameterState, parameterStock)

        if len(listaProductos) > 0:

            header = ['idPro','Nombre', 'Descripcion', 'PC', 'PV', 'G', 'Estado', 'Cant', 'Cantidad Min',
                      'idMar', 'Marca', 'idRubro', 'Rubro', 'idProv', 'Proveedor']
            self.tablaModel = MyTableModel(self.winPrincipal.tvProductos_p, listaProductos, header)
            self.winPrincipal.tvProductos_p.setModel(self.tablaModel)
            self.winPrincipal.tvProductos_p.selectionModel().currentChanged.connect(self.changeSelectedTable)

            self.winPrincipal.tvProductos_p.setColumnHidden(0, True)
            self.winPrincipal.tvProductos_p.setColumnWidth(1, 200)
            self.winPrincipal.tvProductos_p.setColumnWidth(2, 320)
            self.winPrincipal.tvProductos_p.setColumnWidth(3, 60)
            self.winPrincipal.tvProductos_p.setColumnWidth(4, 60)
            self.winPrincipal.tvProductos_p.setColumnWidth(5, 60)
            self.winPrincipal.tvProductos_p.setColumnHidden(6, True)
            self.winPrincipal.tvProductos_p.setColumnWidth(7, 40)
            self.winPrincipal.tvProductos_p.setColumnHidden(8, True)
            self.winPrincipal.tvProductos_p.setColumnHidden(9, True)
            self.winPrincipal.tvProductos_p.setColumnWidth(10, 130)
            self.winPrincipal.tvProductos_p.setColumnHidden(11, True)
            self.winPrincipal.tvProductos_p.setColumnWidth(12, 130)
            self.winPrincipal.tvProductos_p.setColumnHidden(13, True)
            self.winPrincipal.tvProductos_p.setColumnWidth(14, 130)
        else:
            self.winPrincipal.tvProductos_p.setModel(None)



    def changeSelectedTable(self, selected, deselected):

        productoList = selected.model().mylist
        productoSelected = productoList[selected.row()]
        self.producto = Producto()
        self.rubro = Rubro()
        self.proveedor = Proveedor()
        self.marca = Marca()

        self.producto.setIdProducto(productoSelected[0])
        self.producto.setNombre(productoSelected[1])
        self.producto.setDescripcion(productoSelected[2])
        self.producto.setPrecioCompra(productoSelected[3])
        self.producto.setPrecioVenta(productoSelected[4])
        self.producto.setGenero(productoSelected[5])
        self.producto.setEstado(productoSelected[6])
        self.producto.setCantidad(productoSelected[7])
        self.producto.setCantidadMinima(productoSelected[8])

        self.marca.setIdMarca(productoSelected[9])
        self.marca.setMarca(productoSelected[10])
        self.producto.setMarca(self.marca)

        self.rubro.setIdRubro(productoSelected[11])
        self.rubro.setRubro(productoSelected[12])
        self.producto.setRubro(self.rubro)

        self.proveedor.setIdProveedor(productoSelected[13])
        self.proveedor.setDescripcion(productoSelected[14])
        self.producto.setProveedor(self.proveedor)


        self.winPrincipal.tvProductos_p.setRowHeight(deselected.row(),33)
        self.winPrincipal.tvProductos_p.setRowHeight(selected.row(),45)

        self.setCampos()
        self.winPrincipal.btnModificar_p.setEnabled(True)
        self.winPrincipal.btnBorrar_p.setEnabled(True)

    def validarBotones(self, button):
        if button == 'AGREGAR' :
            self.winPrincipal.wDatosProducto.setEnabled(True)
            self.winPrincipal.btnBorrar_p.setEnabled(True)
            self.winPrincipal.btnBorrar_p.setText('CANCELAR')
            self.winPrincipal.btnGuardar_p.setEnabled(True)
            self.winPrincipal.btnModificar_p.setEnabled(False)
            self.winPrincipal.btnAgregar_p.setEnabled(False)
            self.winPrincipal.tvProductos_p.setEnabled(False)
            self.limpiarCampos()

        elif button=='GUARDAR':
            self.winPrincipal.btnModificar_p.setEnabled(False)
            self.winPrincipal.btnAgregar_p.setEnabled(True)
            self.winPrincipal.btnGuardar_p.setEnabled(False)
            self.winPrincipal.btnBorrar_p.setText('BORRAR')
            self.winPrincipal.btnBorrar_p.setEnabled(False)
            self.winPrincipal.tvProductos_p.setEnabled(True)
            self.winPrincipal.wDatosProducto.setEnabled(False)
            self.limpiarCampos()

        elif button == 'MODIFICAR':
            self.winPrincipal.btnModificar_p.setEnabled(False)
            self.winPrincipal.btnAgregar_p.setEnabled(False)
            self.winPrincipal.btnGuardar_p.setEnabled(True)
            self.winPrincipal.btnBorrar_p.setText('CANCELAR')
            self.winPrincipal.btnBorrar_p.setEnabled(True)
            self.winPrincipal.tvProductos_p.setEnabled(False)
            self.winPrincipal.wDatosProducto.setEnabled(True)

        elif button=='BORRAR':
            self.winPrincipal.btnModificar_p.setEnabled(False)
            self.winPrincipal.btnAgregar_p.setEnabled(True)
            self.winPrincipal.btnGuardar_p.setEnabled(False)
            self.winPrincipal.btnBorrar_p.setText('BORRAR')
            self.winPrincipal.btnBorrar_p.setEnabled(False)
            self.winPrincipal.tvProductos_p.setEnabled(True)
            self.winPrincipal.wDatosProducto.setEnabled(False)
            self.limpiarCampos()


    def insertarProducto(self):
        self.conexionProducto.insertarProducto(producto=self.producto)
        self.cargarTabla()

    def modificarProducto(self):
        self.conexionProducto.modificarProducto(self.producto)
        self.cargarTabla()

    def limpiarCampos(self):
        self.winPrincipal.txtNombre_p.setText('')
        self.winPrincipal.txtPrecioCompra_p.setText('')
        self.winPrincipal.txtPrecioVenta_p.setText('')
        self.winPrincipal.sbCantidad_p.setValue(0)
        self.winPrincipal.sbCantidadMin_p.setValue(0)
        self.winPrincipal.txtProveedor_p.setText('')
        self.winPrincipal.txtDescripcion_p.setText('')
        self.winPrincipal.txtRubro_p.setText('')
        self.winPrincipal.txtMarca_p.setText('')
        self.completerProveedor.setCurrentRow(0)
        self.completerRubro.setCurrentRow(0)
        self.completerMarca.setCurrentRow(0)
        self.winPrincipal.txtFilterProductos_p.setText('')
        self.winPrincipal.tvProductos_p.setModel(None)

        self.winPrincipal.txtFilterProductos_p.setFocus(True)

    def setCampos(self):
        self.winPrincipal.txtNombre_p.setText(self.producto.getNombre())
        self.winPrincipal.txtDescripcion_p.setText(str(self.producto.getDescripcion()))
        self.winPrincipal.txtPrecioCompra_p.setText(str(self.producto.getPrecioCompra()))
        self.winPrincipal.txtPrecioVenta_p.setText(str(self.producto.getPrecioVenta()))
        self.winPrincipal.txtProveedor_p.setText(str(self.producto.getProveedor().getDescripcion()))

        self.winPrincipal.sbCantidad_p.setValue(int(self.producto.getCantidad()))
        self.winPrincipal.sbCantidadMin_p.setValue(int(self.producto.getCantidadMinima()))

        self.winPrincipal.txtRubro_p.setText(str(self.producto.getRubro().getRubro()))
        self.winPrincipal.txtMarca_p.setText(str(self.producto.getMarca().getMarca()))

        if self.producto.getEstado() == 1:
            self.winPrincipal.cbEstado_p.setCurrentIndex(0)
        else:
            self.winPrincipal.cbEstado_p.setCurrentIndex(1)

        if self.producto.getGenero() == 'F':
            self.winPrincipal.rbFemenino_p.setChecked(True)
        elif self.producto.getGenero() == 'M':
            self.winPrincipal.rbMasculino_p.setChecked(True)
        else:
            self.winPrincipal.rbIndiferente_p.setChecked(True)




    def validar(self):
        mensaje=''
        if self.winPrincipal.txtNombre_p.text() == '':
            mensaje= "Falta ingresar Nombre"
        elif self.winPrincipal.txtPrecioCompra_p.text() =='':
            mensaje= "Falta ingresar Precio de Compra"
        elif self.winPrincipal.txtPrecioVenta_p.text() =='':
            mensaje= "Falta ingresar Precio de Venta"
        elif self.completerProveedor.currentCompletion() =='':
            mensaje= "Falta ingresar un Proveedor"
        elif self.completerMarca.currentCompletion() == '':
            mensaje = "Falta seleccionar la marca"
        elif self.completerRubro.currentCompletion() == '':
            mensaje = 'Falta seleccionar el rubro'


        """elif self.completerProveedor.currentCompletion() =='' or self.completerProveedor.currentRow() == 0:
            mensaje= "Falta ingresar un Proveedor"
        elif self.completerMarca.currentCompletion() == '' or self.completerMarca.currentRow() == 0:
            mensaje = "Falta seleccionar la marca"
        elif self.completerRubro.currentCompletion() == '' or self.completerRubro.currentRow() == 0:
            mensaje = 'Falta seleccionar el rubro'
        """
        return mensaje

    def setCompleterMarca(self):
        listMarcas = self.conexionProducto.listMarcas()
        self.completerMarca = QCompleter(listMarcas)

        self.completerMarca.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.winPrincipal.txtMarca_p.setCompleter(self.completerMarca)


    def setCompleterRubro(self):
        listRubros = self.conexionProducto.listRubro()
        self.completerRubro = QCompleter(listRubros)
        #self.completerRubro.dynamicPropertyNames()
        self.completerRubro.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.winPrincipal.txtRubro_p.setCompleter(self.completerRubro)



    def setCompleterProveedor(self):
        listProveedores = self.conexionProducto.listProveedor()
        self.completerProveedor = QCompleter(listProveedores)

        self.completerProveedor.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.winPrincipal.txtProveedor_p.setCompleter(self.completerProveedor)