Exemple #1
0
class Filter(QLineEdit):
    """
    Filter widget to display a little X button on the right of the field if ever something's inside
    """
    def __init__(self, *args):
        super(Filter, self).__init__(*args)
        self.textChanged.connect(self.isClean)

        self.clear_button = QPushButton('x', self)
        self.clear_button.setVisible(False)
        self.clear_button.setCursor(Qt.ArrowCursor)
        self.clear_button.clicked.connect(self.clear)

    def isClean(self, text):
        """ Check the emptyness of the field """
        self.clear_button.setVisible(text != '')

    def resizeEvent(self, e):
        super(Filter, self).resizeEvent(e)
        self.clear_button.setGeometry(self.width() - 18, 2, 16, 16)
Exemple #2
0
class Filter(QLineEdit):
    """
    Filter widget to display a little X button on the right of the field if ever something's inside
    """
    def __init__(self, *args):
        super(Filter, self).__init__(*args)
        self.textChanged.connect(self.isClean)

        self.clear_button = QPushButton('x', self)
        self.clear_button.setVisible(False)
        self.clear_button.setCursor(Qt.ArrowCursor)
        self.clear_button.clicked.connect(self.clear)

    def isClean(self, text):
        """ Check the emptyness of the field """
        self.clear_button.setVisible(text != '')

    def resizeEvent(self, e):
        super(Filter, self).resizeEvent(e)
        self.clear_button.setGeometry(self.width() - 18, 2, 16, 16)
class LandmarkWidget(QWidget):
	"""
	LandmarkWidget
	"""

	pickedPosition = Signal()

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

		self.histogramWidget = TrackingHistogramWidget()
		self.button = QPushButton("Pick current landmark position")
		self.button.clicked.connect(self.applyButtonClicked)
		self.button.setVisible(False)

		layout = QGridLayout()
		layout.setAlignment(Qt.AlignTop)
		layout.addWidget(QLabel("Ray profile:"))
		layout.addWidget(self.histogramWidget)
		layout.addWidget(self.button)

		self.setLayout(layout)

	def setSamples(self, samples, scope=None):
		self.histogram = Histogram()
		self.histogram.bins = samples
		
		if scope:
			self.histogram.minY = scope[0]
			self.histogram.maxY = scope[1]
			self.histogram.enabled = True

		self.histogramWidget.setHistogram(self.histogram)
		self.histogramWidget.setAxeMode(left=HistogramWidget.AxeNormal)
		self.histogramWidget.nodeItem.tracking = True
		self.button.setVisible(True)

	@Slot()
	def applyButtonClicked(self):
		self.pickedPosition.emit()

	@Slot()
	def pickedLocation(self, location):
		self.histogramWidget.nodeItem.tracking = False
		self.button.setVisible(False)
Exemple #4
0
class TwoStepLandmarkWidget(QWidget):
    """
	TwoStepLandmarkWidget
	"""

    pickedPosition = Signal()

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

        self.textFrame = QTextEdit(
            "<p>Place your mouse over the desired "
            "landmark point. Press 'Space' to shoot a ray through the volume. "
            "Move the volume around and move the mouse to move the locator. "
            "Press 'Space' again to define the final place of the landmark.</p>"
            "<p>You can also use the ray profile to define the landmark's location.</p>"
        )
        self.textFrame.setReadOnly(True)
        self.textFrame.setFrameShape(QFrame.NoFrame)
        self.textFrame.setAutoFillBackground(False)
        self.textFrame.setAttribute(Qt.WA_TranslucentBackground)
        self.textFrame.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.textFrame.setStyleSheet("background: #aaa")

        self.histogramWidget = TrackingHistogramWidget()
        self.histogramWidget.setMinimumHeight(100)
        self.histogramWidget.setVisible(False)

        self.button = QPushButton("Pick current landmark position")
        self.button.clicked.connect(self.applyButtonClicked)
        self.button.setVisible(False)

        layout = QGridLayout()
        layout.setAlignment(Qt.AlignTop)
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.textFrame)
        layout.addWidget(self.histogramWidget)
        layout.addWidget(self.button)
        self.setLayout(layout)

    def setSamples(self, samples, scope=None):
        self.textFrame.setVisible(False)
        self.histogramWidget.setVisible(True)

        self.histogram = Histogram()
        self.histogram.bins = samples

        if scope:
            self.histogram.minY = scope[0]
            self.histogram.maxY = scope[1]
            self.histogram.enabled = True

        self.histogramWidget.setHistogram(self.histogram)
        self.histogramWidget.setAxeMode(left=HistogramWidget.AxeNormal)
        self.histogramWidget.nodeItem.tracking = True
        self.button.setVisible(True)

    @Slot()
    def applyButtonClicked(self):
        self.pickedPosition.emit()

    @Slot()
    def pickedLocation(self, location):
        self.histogramWidget.nodeItem.tracking = False
        self.button.setVisible(False)
Exemple #5
0
class MainWindow(QMainWindow):  # Sets up the main window
    def resize_window(self):  # Function for resizing the window
        self.resize(self.minimumSizeHint())

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

        # Set window Icon
        self.setWindowTitle(__appname__)
        iconImage = QImage(iconByteArray)
        iconPixmap = QPixmap(iconImage)
        self.setWindowIcon(QIcon(iconPixmap))

        # Set up private key format widgets
        privateKeyFormatLayout = QHBoxLayout()
        privateKeyFormatLabel = QLabel('Select Key Format: ')
        self.privateKeyTypeCombobox = QComboBox()
        self.privateKeyTypeCombobox.addItems(privateKeyFormats)
        self.privateKeyLengthLabel = QLabel('0')
        privateKeyFormatLayout.addWidget(privateKeyFormatLabel)
        privateKeyFormatLayout.addWidget(self.privateKeyTypeCombobox)
        privateKeyFormatLayout.addWidget(self.privateKeyLengthLabel)

        # Set up private key text widgets
        privateKeyLayout = QVBoxLayout()
        privateKeyButtonsLayout = QHBoxLayout()
        generatePrivateKeyButton = QPushButton('Generate Key')
        generatePrivateKeyButton.clicked.connect(self.get_private_key)
        self.copyPrivateKeyButton = QPushButton('Copy Key')
        self.copyPrivateKeyButton.setDisabled(True)
        self.copyPrivateKeyButton.clicked.connect(self.copy_private_key)
        privateKeyButtonsLayout.addWidget(generatePrivateKeyButton)
        privateKeyButtonsLayout.addWidget(self.copyPrivateKeyButton)
        self.privateKeyEdit = GrowingTextEdit()
        self.privateKeyEdit.setFont(QFont('Courier'))
        self.privateKeyEdit.textChanged.connect(
            self.private_key_or_code_changed)
        privateKeyLayout.addLayout(privateKeyButtonsLayout)
        privateKeyLayout.addWidget(self.privateKeyEdit)

        # Set up cypher code widgets
        codeLayout = QHBoxLayout()
        codeLabel = QLabel('Select Cypher Code: ')
        self.codeSelect = QSpinBox()
        self.codeSelect.setValue(10)
        self.codeSelect.setMinimum(2)
        self.codeSelect.setDisabled(True)
        self.codeSelect.valueChanged.connect(self.private_key_or_code_changed)
        codeLayout.addWidget(codeLabel)
        codeLayout.addWidget(self.codeSelect)

        # Set up cypher text widgets
        cypherLayout = QVBoxLayout()
        cypherButtonsLayout = QHBoxLayout()
        cardButtonsLayout = QHBoxLayout()
        self.generateCypherButton = QPushButton('Generate Cypher')
        self.generateCypherButton.clicked.connect(self.get_cypher)
        self.generateCypherButton.setDisabled(True)
        self.copyCypherButton = QPushButton('Copy Cypher')
        self.copyCypherButton.setDisabled(True)
        self.copyCypherButton.clicked.connect(self.copy_cypher)
        cypherButtonsLayout.addWidget(self.generateCypherButton)
        cypherButtonsLayout.addWidget(self.copyCypherButton)
        self.cypherEdit = GrowingTextEdit()
        self.cypherEdit.setFont(QFont('Courier'))
        self.cypherEdit.setReadOnly(True)
        self.cypherEdit.setVisible(False)
        self.cypherEdit.textChanged.connect(self.resize_window)
        self.cypherPreviewLabel = QLabel('-CYPHER PREVIEW-')
        self.cypherPreviewLabel.setAlignment(Qt.AlignCenter)
        self.cypherPreviewLabel.setVisible(False)
        self.cypherPreview = GrowingTextEdit()
        self.cypherPreview.setFont(QFont('Courier'))
        self.cypherPreview.setAlignment(Qt.AlignHCenter)
        self.cypherPreview.setWordWrapMode(QTextOption.NoWrap)
        self.cypherPreview.setReadOnly(True)
        self.cypherPreview.setVisible(False)
        self.cypherCardsPrintButton = QPushButton('Print Cypher Cards')
        self.cypherCardsPrintButton.setVisible(False)
        self.cypherCardsPrintButton.clicked.connect(partial(self.cards, True))
        self.cypherCardsCopyButton = QPushButton('Copy Cypher Cards')
        self.cypherCardsCopyButton.setVisible(False)
        self.cypherCardsCopyButton.clicked.connect(partial(self.cards, False))
        cardButtonsLayout.addWidget(self.cypherCardsPrintButton)
        cardButtonsLayout.addWidget(self.cypherCardsCopyButton)
        cypherLayout.addLayout(cypherButtonsLayout)
        cypherLayout.addWidget(self.cypherEdit)
        cypherLayout.addWidget(self.cypherPreviewLabel)
        cypherLayout.addWidget(self.cypherPreview)
        cypherLayout.addLayout(cardButtonsLayout)

        # Set up donation widgets
        donationsLayout = QVBoxLayout()
        separater = QFrame()
        separater.setFrameShape(QFrame.HLine)
        self.donationButton = QPushButton('Donate')
        self.donationButton.setVisible(False)
        self.donationButton.clicked.connect(self.donate)
        self.copyEthAddressButton = QPushButton('ETH: Copy Address')
        self.copyEthAddressButton.clicked.connect(
            self.copy_eth_donation_address)
        self.copyEthAddressButton.setVisible(False)
        self.copyBtcAddressButton = QPushButton('BTC: Copy Address')
        self.copyBtcAddressButton.clicked.connect(
            self.copy_btc_donation_address)
        self.copyBtcAddressButton.setVisible(False)
        donationsLayout.addWidget(separater)
        donationsLayout.addWidget(self.donationButton)
        donationsLayout.addWidget(self.copyEthAddressButton)
        donationsLayout.addWidget(self.copyBtcAddressButton)

        # Add all widgets and sub-layouts to the master layout
        self.master_layout = QVBoxLayout()
        self.master_layout.addLayout(privateKeyFormatLayout)
        self.master_layout.addLayout(privateKeyLayout)
        self.master_layout.addLayout(codeLayout)
        self.master_layout.addLayout(cypherLayout)
        self.master_layout.addLayout(donationsLayout)
        self.master_widget = QWidget()
        self.master_widget.setLayout(self.master_layout)
        self.setCentralWidget(self.master_widget)

        # Start and connect the window resizing thread
        self.worker = Worker()
        self.worker.updateWindowSize.connect(self.resize_window)

    def copy_private_key(
            self):  # Copies the private key text to the system clipboard
        clip.setText(self.privateKeyEdit.toPlainText())
        self.copyPrivateKeyButton.setText('Key Copied')
        app.processEvents()
        sleep(2)
        self.copyPrivateKeyButton.setText('Copy Key')

    def copy_cypher(self):  # Copies the cypher text to the system clipboard
        clip.setText(self.cypherEdit.toPlainText())
        self.copyCypherButton.setText('Cypher Copied')
        app.processEvents()
        sleep(2)
        self.copyCypherButton.setText('Copy Cypher')

    def copy_eth_donation_address(
            self):  # Copies the ETH donation address to the system clipboard
        clip.setText(ethDonationAddress)
        self.copyEthAddressButton.setText('ETH: Address Copied\nThanks!')
        app.processEvents()
        sleep(2)
        self.copyEthAddressButton.setText('ETH: Copy Address')

    def copy_btc_donation_address(
            self):  # Copies the BTC donation address to the system clipboard
        clip.setText(btcDonationAddress)
        self.copyBtcAddressButton.setText('BTC: Address Copied\nThanks!')
        app.processEvents()
        sleep(2)
        self.copyBtcAddressButton.setText('BTC: Copy Address')

    def get_private_key(
        self
    ):  # Generates a key of the desired format using two instances of the SystemRandom function
        privateKey = generate_private_key.start(
            self.privateKeyTypeCombobox.currentText())
        self.privateKeyEdit.setText(privateKey)
        self.private_key_or_code_changed()
        self.copyPrivateKeyButton.setDisabled(False)

    def private_key_or_code_changed(
        self
    ):  # Changes visibility and ability of some widgets based on user input
        self.privateKeyLengthLabel.setText(
            str(len(self.privateKeyEdit.toPlainText())))
        self.copyCypherButton.setDisabled(True)
        self.cypherEdit.setText('')
        self.cypherPreview.setText('')
        self.cypherEdit.setVisible(False)
        self.cypherPreviewLabel.setVisible(False)
        self.cypherPreview.setVisible(False)
        if len(self.privateKeyEdit.toPlainText()) <= 2:
            self.copyPrivateKeyButton.setDisabled(True)
            self.generateCypherButton.setDisabled(True)
            self.codeSelect.setDisabled(True)
        else:
            self.codeSelect.setMaximum(
                len(self.privateKeyEdit.toPlainText()) - 1)
            self.copyPrivateKeyButton.setDisabled(False)
            self.generateCypherButton.setDisabled(False)
            self.codeSelect.setDisabled(False)
        self.cypherCardsPrintButton.setDisabled(True)
        self.cypherCardsPrintButton.setVisible(False)
        self.cypherCardsCopyButton.setDisabled(True)
        self.cypherCardsCopyButton.setVisible(False)
        self.worker.start()

    def get_cypher(
        self
    ):  # Converts the raw key into a cypher based on the codeSelect value
        if not 1 >= len(self.privateKeyEdit.toPlainText()) >= int(
                self.privateKeyLengthLabel.text()):
            self.generateCypherButton.setDisabled(False)
            cypherRows, cypherSeed = create_cypher.start(
                self.privateKeyEdit.toPlainText(), self.codeSelect.value())
            self.copyCypherButton.setDisabled(False)
            self.cypherEdit.setVisible(True)
            self.cypherEdit.setText(cypherSeed)
            self.cypherPreviewLabel.setVisible(True)
            self.cypherPreview.setVisible(True)
            previewText = ''
            for i in cypherRows:
                previewText += i + '\n'
            self.cypherPreview.setText(previewText)
            self.worker.start()
            self.cypherCardsPrintButton.setDisabled(False)
            self.cypherCardsPrintButton.setVisible(True)
            self.cypherCardsCopyButton.setDisabled(False)
            self.cypherCardsCopyButton.setVisible(True)
            self.donationButton.setVisible(True)
        else:
            self.generateCypherButton.setDisabled(True)

    def cards(self, print):  # Creates and prints the output.txt file
        cardList = split_cypher_into_pairs.start(self.cypherEdit.toPlainText())
        printString = format_cards.start(cardList)
        if print:
            self.cypherCardsPrintButton.setText('Printing')
            app.processEvents()
            cards_output.start(printString)
            self.cypherCardsPrintButton.setText('Print Cypher Cards')
        else:
            clip.setText(printString)
            self.cypherCardsCopyButton.setText('Cards Copied')
            app.processEvents()
            sleep(2)
            self.cypherCardsCopyButton.setText('Copy Cypher Cards')

    def donate(self):  # Adjusts the visibility of the donation buttons
        if self.donationButton.text() == 'Donate':
            self.copyEthAddressButton.setVisible(True)
            self.copyBtcAddressButton.setVisible(True)
            self.donationButton.setText('Hide')
        elif self.donationButton.text() == 'Hide':
            self.copyEthAddressButton.setVisible(False)
            self.copyBtcAddressButton.setVisible(False)
            self.donationButton.setText('Donate')
        self.worker.start()

    def cleanup(self):  # Clears the clipboard of any copied text
        clip.setText('')
class TwoStepLandmarkWidget(QWidget):
	"""
	TwoStepLandmarkWidget
	"""

	pickedPosition = Signal()

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

		self.textFrame = QTextEdit("<p>Place your mouse over the desired "
			"landmark point. Press 'Space' to shoot a ray through the volume. "
			"Move the volume around and move the mouse to move the locator. "
			"Press 'Space' again to define the final place of the landmark.</p>"
			"<p>You can also use the ray profile to define the landmark's location.</p>")
		self.textFrame.setReadOnly(True)
		self.textFrame.setFrameShape(QFrame.NoFrame)
		self.textFrame.setAutoFillBackground(False)
		self.textFrame.setAttribute(Qt.WA_TranslucentBackground)
		self.textFrame.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
		self.textFrame.setStyleSheet("background: #aaa")

		self.histogramWidget = TrackingHistogramWidget()
		self.histogramWidget.setMinimumHeight(100)
		self.histogramWidget.setVisible(False)

		self.button = QPushButton("Pick current landmark position")
		self.button.clicked.connect(self.applyButtonClicked)
		self.button.setVisible(False)

		layout = QGridLayout()
		layout.setAlignment(Qt.AlignTop)
		layout.setSpacing(0)
		layout.setContentsMargins(0, 0, 0, 0)
		layout.addWidget(self.textFrame)
		layout.addWidget(self.histogramWidget)
		layout.addWidget(self.button)
		self.setLayout(layout)

	def setSamples(self, samples, scope=None):
		self.textFrame.setVisible(False)
		self.histogramWidget.setVisible(True)

		self.histogram = Histogram()
		self.histogram.bins = samples
		
		if scope:
			self.histogram.minY = scope[0]
			self.histogram.maxY = scope[1]
			self.histogram.enabled = True

		self.histogramWidget.setHistogram(self.histogram)
		self.histogramWidget.setAxeMode(left=HistogramWidget.AxeNormal)
		self.histogramWidget.nodeItem.tracking = True
		self.button.setVisible(True)

	@Slot()
	def applyButtonClicked(self):
		self.pickedPosition.emit()

	@Slot()
	def pickedLocation(self, location):
		self.histogramWidget.nodeItem.tracking = False
		self.button.setVisible(False)
Exemple #7
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        #widnow setup
        resolution = QDesktopWidget().screenGeometry()
        self.screen_w = resolution.width()
        self.screen_h = resolution.height()
        self.setGeometry(0, 0, 650, 200)
        self.setWindowTitle('bento dumper' + mof.get_version_suffix())
        self.setWindowIcon(QIcon('icons/run.png'))

        #center window
        qr = self.frameGeometry()
        cp = QtGui.QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())
        #adjust size
        self.resize(self.screen_w / 2, self.screen_h / 16)
        self.Menu()
        self.Layout()

        central_widget = QtGui.QWidget()
        central_widget.setLayout(self.main_layout)
        self.setCentralWidget(central_widget)

    def Menu(self):
        #this creates an action exit, a shortcut and status tip
        exitAction = QAction(QIcon('icons/exit.png'), '&Exit', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.setStatusTip('Exit application')
        exitAction.triggered.connect(self.close)

        openFile = QAction(QIcon('icons/open.png'), '&Open', self)
        openFile.setShortcut('Ctrl+O')
        openFile.setStatusTip('Open new File')
        openFile.triggered.connect(self.browse)

        runAction = QAction(QIcon('icons/run.png'), '&Run', self)
        runAction.setShortcut('Ctrl+R')
        runAction.setStatusTip('Run Mars')
        runAction.triggered.connect(self.run_event)

    def Layout(self):
        #LAYOUT
        self.directory_prompt = QLabel(self)
        self.directory_prompt.setText("Directory Selected:")
        self.directory_prompt.move(25, 50)
        self.directory_prompt.resize(150, 30)

        self.browse_btn = QPushButton("Browse", self)
        self.browse_btn.move(130, 50)
        self.browse_btn.setStatusTip(" Browse Folder")
        # self.browse_btn.setStyleSheet("background-color: rgb(186, 186, 186); border-radius: 15px;border-style: solid;border-width: 2px;border-color: black;");
        self.browse_btn.clicked.connect(self.browse)

        self.dir_shower = QLabel(self)
        self.dir_shower.setText("Directory")

        self.run_mars = QPushButton("Dump BENTO", self)
        self.run_mars.setVisible(True)
        self.run_mars.move(25, 160)
        self.run_mars.resize(self.screen_w / 2 - 150, 50)
        self.run_mars.setStatusTip('')
        self.run_mars.setStyleSheet(
            "background-color: rgb(142, 229, 171); border-radius: 15px;")
        self.run_mars.clicked.connect(self.run_event)

        self.menu_layout = QtGui.QHBoxLayout()
        self.menu_layout.addWidget(self.browse_btn)
        self.menu_layout.addWidget(self.directory_prompt)
        self.menu_layout.addWidget(self.dir_shower)
        self.menu_layout.addStretch()

        self.run_layout = QtGui.QHBoxLayout()
        self.run_layout.addWidget(self.run_mars)

        self.main_layout = QtGui.QVBoxLayout()
        self.main_layout.addLayout(self.menu_layout)
        self.main_layout.addLayout(self.run_layout)

        self.main_layout.addStretch()

    def browse(self):
        # sender = self.sender()
        dialog = QtGui.QFileDialog()
        dialog.setFileMode(QtGui.QFileDialog.Directory)
        dialog.setOption(QtGui.QFileDialog.ShowDirsOnly)
        self.dirname = dialog.getExistingDirectory(self, 'Choose Directory',
                                                   os.path.curdir)
        if os.path.exists(self.dirname) and os.path.isdir(self.dirname):
            self.dir_shower.setText(self.dirname)
            return
        else:
            QMessageBox.information(self, " Wrong file selected",
                                    "Select a folder containing .seq files!")

    def run_event(self):
        self.genericThread = GenericThread(self.dirname)
        self.genericThread.start()
Exemple #8
0
class BookEditForm(QScrollArea, Ui_BookForm):
    """ Interface for book edit """

    column = {
            'id':0, 'barcode':1, 'title':2, 'author':3, 's_author':4, 'publisher':5, 'year':6, 'price':7,
            'description':8, 'stock':9, 'image':10, 'availability':11 }
    IMG_SIZE = (150, 150)

    def __init__(self, record_id, parent=None):
        super(BookEditForm, self).__init__(parent)
        self.setupUi(self)
        # had to subclass this spinbox to support return grabbing
        self.edYear = ReturnKeySpinBox(self)
        self.edYearHolder.addWidget(self.edYear)
        # configuring id's for radio group
        self.radioAvailability.setId(self.rdSell,0)
        self.radioAvailability.setId(self.rdRent,1)
        self.radioAvailability.setId(self.rdInactive,2)
        # overlaying a clean button over the image (couldn't do it in designer)
        self.btnCleanImage = QPushButton()
        self.btnCleanImage.setIcon(QIcon(":icons/clean.png"))
        self.btnCleanImage.setFixedWidth(35)
        self.btnCleanImage.clicked.connect(self.clear_image)
        self.btnCleanImage.setVisible(False)
        clean_img_layout = QVBoxLayout(self.edImage)
        clean_img_layout.addWidget(self.btnCleanImage)
        clean_img_layout.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        clean_img_layout.setContentsMargins(2,2,0,0)

        self._access = statics.access_level
        # for currency formatting
        self._locale = QLocale()
        self._record_id = record_id

        # had to hardcode these, wouldn't work otherwise:
        self.contentsLayout.setAlignment(self.groupBox, QtCore.Qt.AlignTop)
        self.contentsLayout.setAlignment(self.groupBox_2, QtCore.Qt.AlignTop)

        self.log = logging.getLogger('BookEditForm')

        self._subject_list = []

        self._removed_subjects = []
        self._added_subjects = []

        self.setup_model()
        self.fill_form()
        self.setup_fields()
        self._old_data = self.extract_input(text_only=True)

        # flag to indicate whether there were changes to the fields
        self._dirty = False
        # user did input an image
        self._image_set = False
        # image changed during edit
        self._image_changed = False

    def is_dirty(self):
        return self._dirty

    def setup_fields(self):
        """ setting up validators and stuff """
        # validators
        # forcing uppercasing on these fields
        self.edTitle.setValidator(UppercaseValidator())
        self.edAuthor.setValidator(UppercaseValidator())
        self.edSAuthor.setValidator(UppercaseValidator())
        self.edPublisher.setValidator(UppercaseValidator())
        self.edPrice.setValidator(CurrencyValidator(self.edPrice))
        self.edBarcode.setValidator(NumericValidator())
        self.edYear.setMinimum(1900)
        self.edYear.setMaximum(date.today().year)
        self.edYear.setValue(date.today().year)
        # fixing tab order
        self.setTabOrder(self.edPublisher, self.edYear)
        self.setTabOrder(self.edYear, self.edPrice)
        # connecting return key to tab
        lineEditList = self.findChildren(QLineEdit)
        for lineEdit in lineEditList:
            # had some problem with C++ originated objects
            if lineEdit.objectName() not in ['qt_spinbox_lineedit', 'edSubject']:
                lineEdit.returnPressed.connect(lineEdit.focusNextChild)
            # detect changes on line edits
            lineEdit.textChanged.connect(self.check_changes)
        # different behaviour for these
        self.edBarcode.textChanged.connect(self.check_barcode)
        self.edSubject.returnPressed.connect(self.on_btnAddSubject_clicked)

        # completers
        config_completer(self.edSubject, self._subject_model, "name")
        config_completer(self.edAuthor, self._author_model, "name")
        config_completer(self.edSAuthor, self._s_author_model, "name")
        config_completer(self.edPublisher, self._publisher_model, "name")

        # making image clickable
        clickable(self.edImage).connect(self.handle_image)

    def fill_form(self):
        # retrieving book info
        self.edBarcode.setText(self._record.value("barcode"))
        self.edTitle.setText(self._record.value("title"))
        self.edYear.setValue(self._record.value("year"))
        self.edDescription.setPlainText(self._record.value("description"))
        self.radioAvailability.button(self._record.value("availability")).setChecked(True)
        # retrieving image
        ba = QByteArray(self._record.value("image"))
        if ba:
            self._image_set = True
            img = qbytearray_to_qimage(ba)
            self.set_image(img, clean_visible=True)
        # currency
        # TODO: ARRUMAR
        self.edPrice.setText(self._locale.toString(self._record.value("price"), 'f', 2).replace('.',''))
        # qcompleter fields
        self.edAuthor.setText(self._get_name_from_id("author", self._record.value("author_id")))
        self.edSAuthor.setText(self._get_name_from_id("s_author", self._record.value("s_author_id")))
        self.edPublisher.setText(self._get_name_from_id("publisher", self._record.value("publisher_id")))

        # retrieving subjects
        for subj_record in self._subj_records:
            self.add_subject([subj_record.value("id"),subj_record.value("name")])

        # clearing changes
        self._added_subjects[:] = []
        self.refresh_tableSubjects()

    def clear_image(self):
        img = QImage(":icons/no_image.png")
        self.set_image(img, clean_visible=False)
        if self._image_set:
            self._image_set = False
        self._image_changed = True

    def handle_image(self):
        image_path = QFileDialog.getOpenFileName(self, "Escolha uma imagem", os.getenv("HOME"), "Imagens (*.png, *.jpg *.bmp)")[0]
        if os.path.exists(image_path):
            self.set_image(QImage(image_path), clean_visible=True)
            self._image_set = True
            self._image_changed = True

    def set_image(self, img, clean_visible=False):
        pix = QPixmap.fromImage(img)
        pix = pix.scaled(self.IMG_SIZE[0], self.IMG_SIZE[1], Qt.KeepAspectRatio)
        self.edImage.setPixmap(pix)
        self.edImage.setScaledContents(True)
        self.btnCleanImage.setVisible(clean_visible)

    def check_changes(self, txt):
        # getting sender info
        sender = self.sender().objectName().split('ed')[1].lower()
        if sender != 'subject' and self._old_data[sender] != txt:
            self._dirty = True

    def check_barcode(self, txt):
        if len(txt) == self.edBarcode.maxLength():
            self.edBarcode.focusNextChild()

    def extract_input(self, text_only=False):
        data = {}
        data['barcode'] = self.edBarcode.text()
        data['title'] = self.edTitle.text()

        # completer fields
        for c_field, line_edit in [("author", self.edAuthor), ("s_author", self.edSAuthor), ("publisher", self.edPublisher)]:
            if not text_only:
                data[c_field] = self._get_cfield_value(c_field, line_edit.text())
            else:
                data[c_field] = line_edit.text()
        data['year'] = self.edYear.value()
        data['price'] = self._locale.toDouble(self.edPrice.text())[0]
        data['description'] = self.edDescription.toPlainText()
        if not text_only and self._image_changed and self._image_set:
            data['image'] = qpixmap_to_qbytearray(self.edImage.pixmap())

        data['availability'] = self.radioAvailability.checkedId()

        return data

    def setup_model(self):
        db = Db_Instance("edit_book").get_instance()
        if not db.open():
            self.log.error(db.lastError().text())
            message = unicode("Erro de conexão\n\n""Banco de dados indisponível".decode('utf-8'))
            QMessageBox.critical(self, unicode("Seareiros - Edição de Livro".decode('utf-8')), message)
        else:
            # book
            self._model = QSqlRelationalTableModel(self, db=db)
            self._model.setTable("book")
            self._model.setFilter("id = " + str(self._record_id))
            # self._model.setRelation(self.column["author"], QSqlRelation("author", "id", "name"))
            # self._model.setRelation(self.column["s_author"], QSqlRelation("s_author", "id", "name"))
            # self._model.setRelation(self.column["publisher"], QSqlRelation("publisher", "id", "name"))
            self._model.select()
            self._record = self._model.record(0)
            # models for setting up qcompleters:
            # book_in_subject
            self._book_in_subj_model = QSqlTableModel(self, db=db)
            self._book_in_subj_model.setTable("book_in_subject")
            # subject
            self._subject_model = QSqlTableModel(self, db=db)
            self._subject_model.setTable("subject")
            self._subject_model.select()
            # author
            self._author_model = QSqlTableModel(self, db=db)
            self._author_model.setTable("author")
            self._author_model.select()
            # s_author
            self._s_author_model = QSqlTableModel(self, db=db)
            self._s_author_model.setTable("s_author")
            self._s_author_model.select()
            # publisher
            self._publisher_model = QSqlTableModel(self, db=db)
            self._publisher_model.setTable("publisher")
            self._publisher_model.select()

            # retrieving current subjects, should probably place this elsewhere but it's related to models
            self._subject_records = []
            sql_statement = """SELECT id, name FROM subject s, book_in_subject b_s
                               WHERE s.id = b_s.subject_id AND b_s.book_id = %s
                            """ % str(self._record_id)
            read_only_subject_model = QSqlQueryModel()
            read_only_subject_model.setQuery(sql_statement, db)
            # checking query validity
            if not read_only_subject_model.lastError().isValid():
                self._subj_records = iterate_model(read_only_subject_model)

    def update_data(self):
        data = self.extract_input()
        # checking fields that aren't inserted yet
        for val, model in [('author', self._author_model), ('s_author', self._s_author_model),
                           ('publisher', self._publisher_model)]:
            if isinstance(data[val], unicode):
                # needs to be inserted
                model.insertRow(0)
                model.setData(model.index(0,1), data[val])
                data[val] = submit_and_get_id(self, model, self.log)
                if not data[val]:
                    # won't proceed if this fails
                    return False
        for key,val in data.items():
            self._model.setData(self._model.index(0, self.column[key]), val)
        if 'image' not in data and self._image_changed:
            # user cleared the image
            ok = self._model.setData(self._model.index(0, self.column['image']), None)
            print ok

        # try to commit changes
        if not self._model.submitAll():
            self.log.error(self._model.lastError().text())
            message = unicode("Erro de transação\n\n""Não foi possível salvar no banco de dados".decode('utf-8'))
            QMessageBox.critical(self, "Seareiros - Edição de Livro", message)
            return False
        else:
            # updating subjects
            error = False
            # added subjects
            for subj in self._added_subjects:
                # the list has the format [id, text] for existing subjects or [None, text] otherwise
                if not subj[0]:
                    # need to insert the subject before associating it with the book
                    self._subject_model.insertRow(0)
                    self._subject_model.setData(self._subject_model.index(0,1), subj[1])
                    subj[0] = submit_and_get_id(self, self._subject_model, self.log)
                    if not subj[0]:
                        error = True
                        break
                # have a valid record id for the subject to be associated
                self._book_in_subj_model.insertRow(0)
                self._book_in_subj_model.setData(self._book_in_subj_model.index(0,0), self._record_id)
                self._book_in_subj_model.setData(self._book_in_subj_model.index(0,1), subj[0])
                ok = self._book_in_subj_model.submitAll()
                if not ok:
                    error = True
                    self.log.error(self._book_in_subj_model.setLastError().text())
                    break
            # removed subjects
            for removed_id in self._removed_subjects:
                self._book_in_subj_model.setFilter("book_id = %s AND subject_id = %s" % (str(self._record_id),str(removed_id)))
                self._book_in_subj_model.select()
                self._book_in_subj_model.removeRow(0)
                if self._book_in_subj_model.lastError().isValid():
                    error = True
                    self.log.error(self._book_in_subj_model.lastError().text())
                    break
            if not error:
                message = unicode("Sucesso!\n\n""O livro foi atualizado com êxito no banco de dados".decode('utf-8'))
                QMessageBox.information(self, unicode("Seareiros - Edição de Livro".decode('utf-8')), message)
            else:
                message = unicode("Erro\n\n""Associado alterado, "
                                  "porém ocorreu um problema ao salvar suas atividades".decode('utf-8'))
                QMessageBox.warning(self, unicode("Seareiros - Edição de Livro".decode('utf-8')), message)
            # if I don't set this flag here it'll trigger a warning for altering data on the form
            self._dirty = False
            return True

    def _get_id_from_name(self, table, name):
        db = Db_Instance(table + "_fetch_" + name + "_id").get_instance()
        if not db.open():
            return None
        else:
            query = QSqlQuery(db)
            query.prepare("SELECT id FROM %s WHERE name = :name" % table)
            query.bindValue(":name", name)
            query.exec_()
            if query.next():
                return query.record().value("id")
            else:
                return None

    def _get_name_from_id(self, table, id):
        db = Db_Instance(table + "_fetch_" + str(id) + "_name").get_instance()
        if not db.open():
            return None
        else:
            query = QSqlQuery(db)
            query.prepare("SELECT name FROM %s WHERE id = :id" % table)
            query.bindValue(":name", id)
            query.exec_()
            if query.next():
                return query.record().value("name")
            else:
                return None

    @QtCore.Slot()
    def on_btnAddSubject_clicked(self):
        txt = self.edSubject.text()
        if txt != '':
            id = self._get_id_from_name('subject', self.edSubject.text())
            if id:
                # known register
                data = [id, txt]
            else:
                # new data
                data = [None, txt]
            not_a_duplicate = self.add_subject(data)
            if not_a_duplicate:
                self.refresh_tableSubjects()
                self.edSubject.clear()
            self.edSubject.setFocus()

    @QtCore.Slot()
    def on_btnCleanSubjects_clicked(self):
        self.clear_table()
        self.edSubject.setFocus()

    def clear_table(self):
        # can't directly change activity_list here
        itens = [i for i in self._subject_list]
        for item in itens:
            self.remove_subject(item)
        self._added_subjects[:] = []

    def add_subject(self, data):
        """ adds a subject to the list except for duplicates """
        if data in self._subject_list:
            return False
        else:
            if self.is_in_del_queue(data[0]):
                self._removed_subjects.remove(data[0])
            else:
                self._added_subjects.append(data)
            self._subject_list.append(data)
            # sorts by name
            self._subject_list.sort(key=operator.itemgetter(1))
            return True

    def refresh_tableSubjects(self):
        if len(self._subject_list) > 0:
            self.tableSubjects.setColumnCount(len(self._subject_list[0])+1)
            col_labels = ["", "Nome", ""]
            self.tableSubjects.setHorizontalHeaderLabels(col_labels)
            self.tableSubjects.setColumnHidden(0, True)
        else:
            self.tableSubjects.setColumnCount(0)
        self.tableSubjects.setRowCount(len(self._subject_list))
        for i, row in enumerate(self._subject_list):
            for j, col in enumerate(row):
                item = QTableWidgetItem(col)
                self.tableSubjects.setItem(i, j, item)
            # icon to remove rows individually
            remove_icon = QIcon(":icons/conn_failed.png")
            remove_btn = QPushButton(remove_icon, "")
            remove_btn.clicked.connect(partial(self.remove_subject, subject=row))
            self.tableSubjects.setCellWidget(i, len(row), remove_btn)
        self.tableSubjects.resizeColumnsToContents()
        self.tableSubjects.horizontalHeader().setResizeMode(1, QHeaderView.Stretch)

    def is_in_del_queue(self, id):
        return id in self._removed_subjects

    def is_in_add_queue(self, data):
        return data in self._added_subjects

    def remove_subject(self, subject):
        # remove a row based on its value
        self._subject_list.remove(subject)
        if self.is_in_add_queue(subject):
             # unqueue previously added activity
            self._added_subjects.remove(subject)
        else:
            id = subject[0]
            if id:
                self._removed_subjects.append(id)
        self.refresh_tableSubjects()

    def _get_cfield_value(self, c_field, text):
        if text == '':
            return None
        id = self._get_id_from_name(c_field, text)
        if id:
            return id
        else:
            return text
class LandmarkLocationWidget(QWidget):
	# Signals
	activated = Signal(int, bool)
	deleted = Signal(int)

	def __init__(self):
		super(LandmarkLocationWidget, self).__init__()
		self._active = False
		self._font = QFont()
		self._font.setPointSize(10)

		self.indexLabel = QLabel()
		self.indexLabel.setMaximumWidth(10)
		self.indexLabel.setMinimumWidth(10)

		self.doneButton = QPushButton("Done")
		self.doneButton.setMaximumWidth(50)
		self.doneButton.setFont(self._font)
		self.doneButton.clicked.connect(self.doneButtonClicked)

		self.deleteButton = QPushButton("X")
		self.deleteButton.setMaximumWidth(15)
		self.deleteButton.setMinimumWidth(15)
		self.deleteButton.setMaximumHeight(15)
		self.deleteButton.setMinimumHeight(15)
		self.deleteButton.setFont(self._font)
		self.deleteButton.setVisible(False)
		self.deleteButton.clicked.connect(self.deleteButtonClicked)

		self.fixedButton = SpecialButton()
		self.fixedButton.setFont(self._font)
		self.movingButton = SpecialButton()
		self.movingButton.setFont(self._font)

		layout = QGridLayout()
		layout.setContentsMargins(0, 0, 0, 0)
		layout.setVerticalSpacing(0)
		layout.addWidget(self.deleteButton, 0, 0)
		layout.addWidget(self.indexLabel, 0, 1)
		layout.addWidget(self.fixedButton, 0, 2)
		layout.addWidget(self.movingButton, 0, 3)
		layout.addWidget(self.doneButton, 0, 4)
		self.setLayout(layout)
		self._updateState()
		
	def setIndex(self, index):
		self.index = index
		self.indexLabel.setText(str(index+1))

	@property
	def active(self):
		return self._active

	@active.setter
	def active(self, value):
		self._active = value
		self._updateState()

	def setLandmarkSet(self, points):
		self.setFixedLandmark(points[0])
		self.setMovingLandmark(points[1])

	def setFixedLandmark(self, landmark):
		if not landmark:
			return
		labelX = "%2.0f" % landmark[0]
		labelY = "%2.0f" % landmark[1]
		labelZ = "%2.0f" % landmark[2]
		self.fixedButton.setText(labelX + ", " + labelY + ", " + labelZ)

	def setMovingLandmark(self, landmark):
		if not landmark:
			return
		labelX = "%2.0f" % landmark[0]
		labelY = "%2.0f" % landmark[1]
		labelZ = "%2.0f" % landmark[2]
		self.movingButton.setText(labelX + ", " + labelY + ", " + labelZ)

	@Slot()
	def doneButtonClicked(self):
		self._active = not self._active
		self.activated.emit(self.index, self._active)
		self._updateState()

	@Slot()
	def deleteButtonClicked(self):
		self.deleted.emit(self.index)

	def _updateState(self):
		text = "Done" if self._active else "Edit"
		self.doneButton.setText(text)
		self.deleteButton.setVisible(self._active)
		self.indexLabel.setVisible(not self._active)
		self.fixedButton.setEnabled(self._active)
		self.movingButton.setEnabled(self._active)
Exemple #10
0
class WasteDish():
    """Dish waste management tab"""
    global logger

    def __init__(self):
        ###
        logger.info('Inside WasteDish')
        self.wastedetail_tab_1 = QWidget()
        self.wastedetail_tab_1.setObjectName("wastedetail_tab_1")
        self.verticalLayout_9 = QVBoxLayout(self.wastedetail_tab_1)
        self.verticalLayout_9.setObjectName("verticalLayout_9")
        self.verticalLayout_8 = QVBoxLayout()
        self.verticalLayout_8.setObjectName("verticalLayout_8")
        self.horizontalLayout_12 = QHBoxLayout()
        self.horizontalLayout_12.setObjectName("horizontalLayout_12")
        self.waste_fromdate_label = QLabel(self.wastedetail_tab_1)
        self.waste_fromdate_label.setObjectName('waste_fromdate_label')
        self.horizontalLayout_12.addWidget(self.waste_fromdate_label)
        self.waste_fromdate_dateedit = QDateEdit(self.wastedetail_tab_1)
        self.waste_fromdate_dateedit.setCalendarPopup(True)
        self.waste_fromdate_dateedit.setObjectName("waste_fromdate_dateedit")
        self.horizontalLayout_12.addWidget(self.waste_fromdate_dateedit)
        self.waste_todate_label = QLabel(self.wastedetail_tab_1)
        self.waste_todate_label.setObjectName('waste_todate_label')
        self.horizontalLayout_12.addWidget(self.waste_todate_label)
        self.waste_todate_dateedit = QDateEdit(self.wastedetail_tab_1)
        self.waste_todate_dateedit.setCalendarPopup(True)
        self.waste_todate_dateedit.setObjectName("waste_todate_dateedit")
        self.waste_todate_dateedit.setMaximumDate(QDate.currentDate())
        self.horizontalLayout_12.addWidget(self.waste_todate_dateedit)
        spacerItem28 = QSpacerItem(40, 20, QSizePolicy.Expanding,
                                   QSizePolicy.Minimum)
        self.horizontalLayout_12.addItem(spacerItem28)
        self.waste_table_search_button = QPushButton(self.wastedetail_tab_1)
        self.waste_table_search_button.setObjectName(
            "waste_table_search_button")
        self.horizontalLayout_12.addWidget(self.waste_table_search_button)
        self.verticalLayout_8.addLayout(self.horizontalLayout_12)
        self.waste_table = QTableWidget(self.wastedetail_tab_1)
        self.waste_table.setObjectName("waste_table")
        self.waste_table.setColumnCount(5)
        self.waste_table.setRowCount(0)
        item = QTableWidgetItem()
        self.waste_table.setHorizontalHeaderItem(0, item)
        item = QTableWidgetItem()
        self.waste_table.setHorizontalHeaderItem(1, item)
        item = QTableWidgetItem()
        self.waste_table.setHorizontalHeaderItem(2, item)
        item = QTableWidgetItem()
        self.waste_table.setHorizontalHeaderItem(3, item)
        item = QTableWidgetItem()
        self.waste_table.setHorizontalHeaderItem(4, item)
        self.waste_table.horizontalHeader().setCascadingSectionResizes(False)
        self.waste_table.horizontalHeader().setStretchLastSection(True)
        self.waste_table.verticalHeader().setVisible(False)
        self.waste_table.verticalHeader().setCascadingSectionResizes(False)
        self.waste_table.setSortingEnabled(True)
        self.verticalLayout_8.addWidget(self.waste_table)
        self.horizontalLayout_13 = QHBoxLayout()
        self.horizontalLayout_13.setObjectName("horizontalLayout_13")
        self.waste_table_newrow_button = QPushButton(self.wastedetail_tab_1)
        self.waste_table_newrow_button.setObjectName(
            "waste_table_newrow_button")
        self.horizontalLayout_13.addWidget(self.waste_table_newrow_button)
        spacerItem29 = QSpacerItem(612, 20, QSizePolicy.Expanding,
                                   QSizePolicy.Minimum)
        self.horizontalLayout_13.addItem(spacerItem29)
        self.waste_table_discard_button = QPushButton(self.wastedetail_tab_1)
        self.waste_table_discard_button.setObjectName(
            "waste_table_discard_button")
        self.horizontalLayout_13.addWidget(self.waste_table_discard_button)
        self.verticalLayout_8.addLayout(self.horizontalLayout_13)
        self.verticalLayout_9.addLayout(self.verticalLayout_8)
        ##retranslate
        self.waste_fromdate_label.setText(
            QApplication.translate("MainWindow", "From Date", None,
                                   QApplication.UnicodeUTF8))
        self.waste_fromdate_dateedit.setDisplayFormat(
            QApplication.translate("MainWindow", "dd/MM/yyyy", None,
                                   QApplication.UnicodeUTF8))
        self.waste_todate_label.setText(
            QApplication.translate("MainWindow", "To Date", None,
                                   QApplication.UnicodeUTF8))
        self.waste_todate_dateedit.setDisplayFormat(
            QApplication.translate("MainWindow", "dd/MM/yyyy", None,
                                   QApplication.UnicodeUTF8))
        self.waste_table_search_button.setText(
            QApplication.translate("MainWindow", "Search", None,
                                   QApplication.UnicodeUTF8))
        self.waste_table.horizontalHeaderItem(0).setText(
            QApplication.translate("MainWindow", "Code", None,
                                   QApplication.UnicodeUTF8))
        self.waste_table.horizontalHeaderItem(1).setText(
            QApplication.translate("MainWindow", "Item", None,
                                   QApplication.UnicodeUTF8))
        self.waste_table.horizontalHeaderItem(2).setText(
            QApplication.translate("MainWindow", "Category", None,
                                   QApplication.UnicodeUTF8))
        self.waste_table.horizontalHeaderItem(3).setText(
            QApplication.translate("MainWindow", "Quantity", None,
                                   QApplication.UnicodeUTF8))
        self.waste_table.horizontalHeaderItem(4).setText(
            QApplication.translate("MainWindow", "Reason", None,
                                   QApplication.UnicodeUTF8))
        self.waste_table_newrow_button.setText(
            QApplication.translate("MainWindow", "New Row", None,
                                   QApplication.UnicodeUTF8))
        self.waste_table_discard_button.setText(
            QApplication.translate("MainWindow", "Discard Item", None,
                                   QApplication.UnicodeUTF8))
        self.wastedetail_tab_1.setTabOrder(self.waste_fromdate_dateedit,
                                           self.waste_todate_dateedit)
        self.wastedetail_tab_1.setTabOrder(self.waste_todate_dateedit,
                                           self.waste_table_search_button)
        ###signals and slots && other stuffs
        # self.mainwindow = Ui_MainWindow  # just for the ease of finding the attributes in pycharm
        self.schedule = SchedulePurchase()
        self.suplier = BusinessParty(category='Supplier')
        self.add = AddStockInventory()
        self.item = WasteMenu()
        self.waste_fromdate_dateedit.setDate(QDate.currentDate())
        self.waste_todate_dateedit.setDate(QDate.currentDate())
        self.waste_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.waste_table_newrow_button.clicked.connect(self.add_new_blank_rows)
        self.waste_table_discard_button.clicked.connect(self.discard)
        self.waste_table_search_button.clicked.connect(self.search_discard)
        self.wastedetail_tab_1.setFocusPolicy(Qt.StrongFocus)
        self.wastedetail_tab_1.focusInEvent = self.load_rows

    def load_rows(self, event=None):
        pass

    def add_new_blank_rows(self):
        """
        deletes the schedules in the database
        """
        table = self.waste_table
        item = table.item(0, 0)
        if item:
            message = QMessageBox.critical(QMessageBox(), 'Warning!',
                                           'This will remove all the entries',
                                           QMessageBox.Ok | QMessageBox.Cancel)
            if message == QMessageBox.Ok:
                table.setRowCount(0)
                table.clearContents()
                self.add_row_to_table('new')
                self.waste_table_discard_button.setVisible(True)
        elif not item:
            self.waste_table_discard_button.setVisible(True)
            self.add_row_to_table('new')

    def add_row_to_table(self, *args):
        """
        complex stuff of auto complete to be added to each combo box
        :return:
        """
        table = self.waste_table
        if args:
            if args[0] != 'new':
                table.clearContents()
                table.setRowCount(0)
                table.setRowCount(len(args))
                for i, j in enumerate(args):
                    code = QTableWidgetItem(j['code'])
                    table.setItem(i, 0, code)
                    item = QTableWidgetItem(j['item'])
                    table.setItem(i, 1, item)
                    category = QTableWidgetItem(j['category'])
                    table.setItem(i, 2, category)
                    quantity = QTableWidgetItem(str(j['quantity']))
                    table.setItem(i, 3, quantity)
                    reason = QTableWidgetItem(j['reason_for_discard'])
                    table.setItem(i, 4, reason)
            if args[0] == 'new':
                row = table.rowCount() + 1
                table.setRowCount(row)
                codeline = QLineEdit()
                codeline.editingFinished.connect(
                    lambda: self.get_details_of_code(row))
                table.setCellWidget(row - 1, 0, codeline)
                itemcombo = QComboBox()
                self.fill_item_list(itemcombo)
                itemcombo.currentIndexChanged.connect(
                    lambda: self.get_details_of_item(row))
                table.setCellWidget(row - 1, 1, itemcombo)
                category = QTableWidgetItem()
                table.setItem(row - 1, 2, category)
                quantity = QLineEdit()
                table.setCellWidget(row - 1, 3, quantity)
                combo = QComboBox()
                combo.addItem("Cancelled")
                combo.addItem("Mishandling")
                combo.addItem("Excess")
                table.setCellWidget(row - 1, 4, combo)
        table.setColumnWidth(0, (table.width() / 5))
        table.setColumnWidth(1, (table.width() / 5))
        table.setColumnWidth(2, (table.width() / 5))
        table.setColumnWidth(3, (table.width() / 5))
        table.horizontalHeader().setStretchLastSection(
            True
        )  # important to resize last section else blank space after last column

    def fill_item_list(self, combo):
        """
        fill the item combo box
        :param combo: the combobox object
        :return: none
        """
        itemfield = combo
        itemfield.setStyleSheet("QAbstractItemView{"
                                "background: #4B77BE;"
                                "}")
        self.item.populate_item(itemfield)

    def get_details_of_code(self, rowcount):
        """
        fills the item, category and units based on the code
        :param rowcount: the row count
        :return: none
        """
        row = rowcount - 1
        table = self.waste_table
        codeline = table.cellWidget(row, 0)
        data = self.item.get_details_of_code(codeline.text())
        item = table.cellWidget(row, 1)
        index = item.findText(data['item'])
        item.setCurrentIndex(index)
        category = table.item(row, 2)
        category.setText(data['category'])

    def get_details_of_item(self, rowcount):
        """
        fills the code, category and units based on the item
        :param rowcount: the row count
        :return: none
        """
        row = rowcount - 1
        table = self.waste_table
        itemcombo = table.cellWidget(row, 1)
        data = self.item.get_details_of_item(itemcombo.currentText())
        code = table.cellWidget(row, 0)
        code.setText(data['code'])
        category = table.item(row, 2)
        category.setText(data['category'])

    def discard(self):
        """
        saves the details in db before printing
        """
        logger.info('WasteDish discard initiated')
        table = self.waste_table
        data = self.get_data()
        if data:
            for i in data:
                status = self.item.discard(i)
                if status:
                    model_index = table.indexFromItem(i['model_item'])
                    row = model_index.row()
                    table.removeRow(row)
                else:
                    msg = QMessageBox.critical(QMessageBox(), "Error!!",
                                               "The item cannot be discarded",
                                               QMessageBox.Ok)
                    if msg == QMessageBox.Ok:
                        return False

    def search_discard(self):
        """
        searches the discard from_date and to_date
        :return:none
        """
        table = self.waste_table
        item = table.cellWidget(0, 0)
        if item:
            msg = QMessageBox.critical(QMessageBox(), 'Warning!',
                                       'This will delete all the rows added',
                                       QMessageBox.Ok | QMessageBox.Cancel)
            if msg == QMessageBox.Cancel:
                return False
        f_date = self.waste_fromdate_dateedit.text()
        from_date = datetime.strptime(f_date, '%d/%m/%Y')
        t_date = self.waste_todate_dateedit.text()
        to_date = datetime.strptime(t_date, '%d/%m/%Y')
        to_date = to_date + timedelta(hours=23, minutes=59, seconds=59)
        dataobj = self.item.find_itemdiscard(from_date=from_date,
                                             to_date=to_date)
        self.add_row_to_table(*dataobj)
        self.waste_table_discard_button.setVisible(False)

    def get_data(self):
        """
        :return: fetches all the data for printing
        """
        table = self.waste_table
        rows = table.rowCount()
        dataobj = []
        for i in range(rows):
            dictionary = {}
            item = table.cellWidget(i, 0) if table.cellWidget(
                i, 0) is not None else table.item(i, 0)
            dictionary['code'] = item.text()
            if dictionary['code'] == '':
                break
            item = table.cellWidget(i, 1).currentText() if table.cellWidget(
                i, 1) is not None else table.item(i, 1).text()
            dictionary['item'] = item
            item = table.cellWidget(i, 2) if table.cellWidget(
                i, 2) is not None else table.item(i, 2)
            dictionary['category'] = item.text()
            item = table.cellWidget(i, 3) if table.cellWidget(
                i, 3) is not None else table.item(i, 3)
            dictionary['quantity'] = item.text()
            if dictionary['quantity'] == '':
                self.show_error('Quantity')
                return False
            item = table.cellWidget(i, 4).currentText() if table.cellWidget(
                i, 4) is not None else table.item(i, 4).text()
            dictionary['reason_for_discard'] = item
            dictionary['model_item'] = table.item(i, 2)
            dataobj.append(dictionary)
        return dataobj

    def show_error(self, text):
        """
        :return: pops up an error
        """
        QMessageBox.critical(QMessageBox(), "Fail!!",
                             "Please Enter %s properly" % text, QMessageBox.Ok)
class TransformationParameterWidget(QWidget):
	"""
	TransformationParameterWidget
	"""

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

		self.cancelButton = QPushButton("Cancel")
		self.cancelButton.clicked.connect(self.cancelButtonClicked)

		self.applyButton = QPushButton("Apply")
		self.applyButton.clicked.connect(self.applyButtonClicked)

		self.mainLayout = QGridLayout()
		self.mainLayout.setSpacing(0)
		self.mainLayout.setContentsMargins(0, 0, 0, 0)

		self.widget = QWidget()
		self.widget.setLayout(self.mainLayout)

		self.showControls(False)

		self.transformationWidget = None
		layout = QGridLayout()
		layout.setAlignment(Qt.AlignTop)
		layout.addWidget(self.widget, 0, 0, 1, 2)
		layout.addWidget(self.cancelButton, 1, 0)
		layout.addWidget(self.applyButton, 1, 1)
		self.setLayout(layout)

	def setTransformationTool(self, transformationTool):
		self.transformationTool = transformationTool

		self.cleanUpTransformWidget()

		self.transformationWidget = self.transformationTool.getParameterWidget()
		self.mainLayout.addWidget(self.transformationWidget)
		self.showControls(True)

	@Slot()
	def cancelButtonClicked(self):
		"""
		Cancels the transform and hides the apply / cancel buttons
		"""
		self.showControls(False)
		self.cleanUpTransformWidget()

		self.transformationTool.cancelTransform()
		self.transformationTool.cleanUp()
		self.transformationTool = None

	@Slot()
	def applyButtonClicked(self):
		"""
		Applies the transform and hides the apply / cancel buttons
		"""
		self.showControls(False)
		self.cleanUpTransformWidget()

		self.transformationTool.applyTransform()
		self.transformationTool.cleanUp()
		self.transformationTool = None

	def showControls(self, show):
		self.widget.setVisible(show)
		self.applyButton.setVisible(show)
		self.cancelButton.setVisible(show)

	def cleanUpTransformWidget(self):
		item = self.mainLayout.takeAt(0)
		if item:
			item.widget().deleteLater()
Exemple #12
0
class MassAttribute_UI(QDialog):
    """
    The main UI
    """
    class Applikator(QObject):
        """
        This is the core applier which toggle the display of the corresponding widget and handling events' connections
        """
        def __init__(self, parent=None):
            super(MassAttribute_UI.Applikator, self).__init__()
            self.root = parent

        def widget_event(self, t):
            """
            Return the correct widget's event depending on attribute's type
            :param t: the attribute's type
            :type  t: str
            :return: the event
            :rtype : Signal
            """
            return {
                'float': self.root.W_EDI_float.valueChanged,
                'enum': self.root.W_EDI_enum.currentIndexChanged,
                'int': self.root.W_EDI_int.valueChanged,
                'bool': self.root.W_EDI_bool.stateChanged,
                'str': self.root.W_EDI_str.textChanged,
                'd3': self.root.W_EDI_d3.valuesChanged,
                'd4': self.root.W_EDI_d4.valuesChanged,
                'color': self.root.W_EDI_color.colorChanged
            }[t]

        def unset_editors(self):
            """
            Toggle off all editors and disconnect the current one
            """
            for widget in (self.root.W_EDI_float, self.root.W_EDI_int,
                           self.root.W_EDI_enum, self.root.W_EDI_bool,
                           self.root.W_EDI_str, self.root.W_EDI_d3,
                           self.root.W_EDI_d4, self.root.W_EDI_color):
                widget.setVisible(False)

            # trying to force disconnection
            try:
                self.widget_event(self.root.ctx).disconnect(
                    self.root.apply_value)
            except (KeyError, RuntimeError):
                pass

        def prepare(applier_name):
            """
            A decorator to prepare the attribute depending on type for the corresponding widget and getting the
            attribute's value
            :param applier_name: attribute's type
            :type  applier_name: str
            """
            def sub_wrapper(func):
                def wrapper(self, attr_path):
                    self.unset_editors()
                    self.root.ctx = applier_name
                    self.root.__getattribute__('W_EDI_%s' %
                                               applier_name).setVisible(True)
                    ret = func(self, cmds.getAttr(attr_path), attr_path)
                    return ret

                return wrapper

            return sub_wrapper

        @staticmethod
        def get_bounds(obj, attr, min_default, max_default):
            """
            Try to retrieve the range for the given attribute, if min or max fail it'll set default values
            :param         obj: the object's name
            :type          obj: str
            :param        attr: attribute's name
            :type         attr: str
            :param min_default: minimum default value
            :param max_default: max default value
            :type  min_default: float | int
            :type  max_default: float | int
            :return: minimum, maximum
            :rtype : tuple
            """
            try:
                assert cmds.attributeQuery(attr, n=obj, mxe=True)
                maxi = cmds.attributeQuery(attr, n=obj, max=True)[0]
            except (RuntimeError, AssertionError):
                maxi = max_default
            try:
                assert cmds.attributeQuery(attr, n=obj, mne=True)
                mini = cmds.attributeQuery(attr, n=obj, min=True)[0]
            except (RuntimeError, AssertionError):
                mini = min_default
            return mini, maxi

        @prepare('float')
        def apply_float(self, value, path):
            """
            Float attribute case
            :param value: attribute's value
            :param  path: attribute's path = obj.attr
            """
            obj, attr = path.split('.', 1)
            self.root.W_EDI_float.setRange(
                *self.get_bounds(obj, attr, -100.0, 100.0))
            self.root.W_EDI_float.setValue(value)

        @prepare('enum')
        def apply_enum(self, value, path):
            """Enum case"""
            self.root.W_EDI_enum.clear()
            obj, attr = path.split('.', 1)
            try:
                enums = [
                    enum.split('=')[0] for enum in cmds.attributeQuery(
                        attr, n=obj, listEnum=True)[0].split(':')
                ]
            except RuntimeError:
                self.apply_int(path)
            else:
                self.root.W_EDI_enum.addItems(enums)
                self.root.W_EDI_enum.setCurrentIndex(
                    enums.index(cmds.getAttr(path, asString=True)))

        @prepare('int')
        def apply_int(self, value, path):
            """Integer case"""
            obj, attr = path.split('.', 1)
            self.root.W_EDI_int.setRange(
                *self.get_bounds(obj, attr, -1000, 1000))
            self.root.W_EDI_int.setValue(value)

        @prepare('bool')
        def apply_bool(self, value, path):
            """Boolean case"""
            self.root.W_EDI_bool.setChecked(value)
            self.root.W_EDI_bool.setText(path.split('.', 1)[1])

        @prepare('str')
        def apply_str(self, value, path):
            """String case"""
            self.root.W_EDI_str.setText(value)

        @prepare('d3')
        def apply_d3(self, value, path):
            """3D array case"""
            self.root.W_EDI_d3.setValues(value[0])

        @prepare('d4')
        def apply_d4(self, value, path):
            """4D array case"""
            self.root.W_EDI_d4.setValues(value[0])

        @prepare('color')
        def apply_color(self, value, path):
            """Color case"""
            try:
                colors = value[0]
                self.root.W_EDI_color.setColor([int(c * 255) for c in colors])
            except TypeError:
                self.apply_int(value, path)

    class Attribute(str):
        """
        A custom string attribute class to ship more information into the string variable
        """
        def __new__(cls, path='', super_type=Object):
            obj, attr = path.split('.', 1)

            str_obj = str.__new__(cls, attr)

            str_obj.obj, str_obj.attr = obj, attr
            str_obj.path = path
            str_obj.super_type = super_type
            str_obj.type = None

            return str_obj

    # static variables to pre-load icons and attributes short names
    ctx_icons = {
        'float': QIcon(':render_decomposeMatrix.png'),
        'enum': QIcon(':showLineNumbers.png'),
        'bool': QIcon(':out_decomposeMatrix.png'),
        'time': QIcon(':time.svg'),
        'byte': QIcon(':out_defaultTextureList.png'),
        'angle': QIcon(':angleDim.png'),
        'string': QIcon(':text.png'),
        'float3': QIcon(':animCurveTA.svg'),
        'float4': QIcon(':animCurveTA.svg'),
        'color': QIcon(':clampColors.svg')
    }

    for ctx in ('doubleLinear', 'double', 'long', 'short'):
        ctx_icons[ctx] = ctx_icons['float']

    ctx_icons['double3'] = ctx_icons['float3']
    ctx_icons['double4'] = ctx_icons['float4']

    ctx_wide = {
        'float': ('float', 'doubleLinear', 'double', 'long', 'short'),
        'enum': ('enum', ),
        'bool': ('bool', ),
        'time': ('time', ),
        'byte': ('byte', ),
        'angle': ('doubleAngle', ),
        'string': ('string', ),
        'float3': ('double3', 'float3'),
        'float4': ('double4', 'float4'),
        'color': ('color', )
    }

    def __init__(self, parent=None):
        super(MassAttribute_UI, self).__init__(parent)
        # Abstract
        self.applier = self.Applikator(self)
        self.selection = []
        self.callback = None
        self.ctx = None
        # storing found attributes' types to avoid double check
        self.solved = {}
        self.setLocale(QLocale.C)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setAttribute(Qt.WA_QuitOnClose)

        self.setFixedWidth(300)
        self.setWindowTitle('Massive Attribute Modifier')

        # UI
        L_main = QVBoxLayout()

        self.WV_title = QLabel('')
        self.WV_title.setVisible(False)
        self.WV_title.setFont(QFont('Verdana', 10))
        self.WV_title.setContentsMargins(0, 0, 0, 7)

        self.WB_select = QPushButton('Select')
        self.WB_select.setVisible(False)
        self.WB_select.setFixedWidth(50)
        self.WB_select.clicked.connect(lambda: cmds.select(self.selection))

        self.WB_update = QPushButton('Update')
        self.WB_update.setFixedWidth(50)
        self.WB_update.clicked.connect(
            lambda: self.update_attributes(cmds.ls(sl=True)))

        self.WV_search = Filter()
        self.WV_search.textChanged.connect(self.filter)

        self.WC_cases = QCheckBox('Case sensitive')
        self.WC_cases.stateChanged.connect(self.filter)

        self.WC_types = QCheckBox('Type filtering')

        self.WL_attrtype = QComboBox()
        self.WL_attrtype.setEnabled(False)

        for i, ctx in enumerate(sorted(self.ctx_wide)):
            self.WL_attrtype.addItem(ctx.title())
            self.WL_attrtype.setItemIcon(i, self.ctx_icons[ctx])

        L_attrtype = line(self.WC_types, self.WL_attrtype)

        self.WC_types.stateChanged.connect(
            partial(self.update_attributes, self.selection))
        self.WC_types.stateChanged.connect(self.WL_attrtype.setEnabled)
        self.WL_attrtype.currentIndexChanged.connect(self.filter)

        self.WC_liveu = QCheckBox('Live')
        self.WC_liveu.stateChanged.connect(self.WB_update.setDisabled)
        self.WC_liveu.stateChanged.connect(self.set_callback)

        self.WC_histo = QCheckBox('Load history')
        self.WC_histo.setChecked(True)
        self.WC_histo.stateChanged.connect(
            partial(self.update_attributes, self.selection))

        self.WC_child = QCheckBox('Children')
        self.WC_child.stateChanged.connect(
            partial(self.update_attributes, self.selection))

        options = group(
            'Options', line(self.WC_cases, L_attrtype),
            line(self.WC_child, self.WC_histo, self.WC_liveu, self.WB_update))
        options.layout().setSpacing(2)

        self.WL_attributes = QTreeWidget()
        self.WL_attributes.setStyleSheet(
            'QTreeView {alternate-background-color: #1b1b1b;}')
        self.WL_attributes.setAlternatingRowColors(True)
        self.WL_attributes.setHeaderHidden(True)
        self.WL_attributes.setRootIsDecorated(False)

        self.objs_attr = set()
        self.shps_attr = set()

        self.W_EDI_float = FloatBox()
        self.W_EDI_int = IntBox()
        self.W_EDI_enum = QComboBox()
        self.W_EDI_bool = QCheckBox()
        self.W_EDI_str = QLineEdit()
        self.W_EDI_d3 = Double3()
        self.W_EDI_d4 = Double4()
        self.W_EDI_color = ColorPicker()

        # Final layout
        L_title = line(self.WV_title, self.WB_select)
        L_title.setStretch(0, 1)
        L_main.addLayout(L_title)
        L_main.setAlignment(Qt.AlignLeft)
        L_main.addWidget(self.WV_search)
        L_main.addWidget(options)
        L_main.addWidget(self.WL_attributes)
        L_edits = col(self.W_EDI_bool, self.W_EDI_int, self.W_EDI_float,
                      self.W_EDI_enum, self.W_EDI_str, self.W_EDI_d3,
                      self.W_EDI_d4, self.W_EDI_color)
        L_edits.setContentsMargins(0, 8, 0, 0)
        L_main.addLayout(L_edits)
        L_main.setStretch(3, 1)
        L_main.setSpacing(2)

        self.appliers = {
            'float': self.applier.apply_float,
            'enum': self.applier.apply_enum,
            'bool': self.applier.apply_bool,
            'time': self.applier.apply_float,
            'byte': self.applier.apply_int,
            'angle': self.applier.apply_float,
            'string': self.applier.apply_str,
            'float3': self.applier.apply_d3,
            'float4': self.applier.apply_d4,
            'color': self.applier.apply_color
        }

        self.setLayout(L_main)

        # final settings
        self.WL_attributes.itemSelectionChanged.connect(self.update_setter)
        self.applier.unset_editors()

    def closeEvent(self, *args, **kwargs):
        self.set_callback(False)

    def set_callback(self, state):
        """
        Toggle selection event callback
        :param state: checkbox's state
        :type  state: bool | int
        """
        if state and not self.callback:
            self.callback = MEventMessage.addEventCallback(
                'SelectionChanged', self.update_attributes)
            self.update_attributes(cmds.ls(sl=True))

        elif not state and self.callback:
            MMessage.removeCallback(self.callback)
            self.callback = None

    @staticmethod
    def format_title(nodes):
        """
        Extract the matching characters from a given nodes selection, if begin matches it will return "joint*" with a
        wildcard when names don't match
        :param nodes: objects' list
        :type  nodes: list | tuple
        :return: the formatted name with the corresponding characters
        :rtype : str
        """
        res = None

        if nodes:
            # we get the first node as a reference
            node = nodes[0]
            # and compare with the other nodes
            subs = [w for w in nodes if w != node]

            l = 1
            valid = True
            # will continue until l (length) match the full name's length or until names don't match
            while l < len(node) and valid:
                for sub in subs:
                    if not sub.startswith(node[:l]):
                        valid = False
                        break

                else:
                    l += 1

            # if matching characters isn't long enough we only display the number of nodes selected
            if l <= 3:
                res = '%i objects' % len(nodes)

            # otherwise showing matching pattern
            elif l < len(node) or len(nodes) > 1:
                res = node[:l - 1] + '* (%i objects)' % len(nodes)

            else:
                res = node

        return res

    @staticmethod
    def get_history(node):
        """
        Extract history for the given node
        :rtype: list
        """
        return cmds.listHistory(node, il=2, pdo=True) or []

    @staticmethod
    def get_shapes(node):
        """
        Extract shape(s) for the given node
        :rtype: list
        """
        return cmds.listRelatives(node, s=True, ni=True, f=True)

    def get_attributes_type(self, attrs):
        """
        For a given list of attributes of type Attribute, will loop through and fill the type parameter of the
         attribute with the corresponding type, if type is invalid or not handled, it'll remove it
        :param attrs: attributes' list
        :type  attrs: [MassAttribute_UI.Attribute]
        :return: cleaned and filled attributes' list
        :rtype: [MassAttribute_UI.Attribute]
        """
        attrs = list(attrs)
        # first we sort the attributes' list
        attrs.sort()

        # then we try to extract the attribute's type
        for i, attr in enumerate(attrs):
            try:
                if attr.attr in self.solved:
                    attr.type = self.solved[attr.attr]
                    raise RuntimeError
                tpe = cmds.getAttr(attr.path, typ=True)
                assert tpe
                attr.type = tpe
                self.solved[attr.attr] = tpe
            except (AssertionError, ValueError, RuntimeError):
                pass

        # defining a to-remove list
        rm_list = set()

        layers = {'3': 'XYZ', '4': 'XYZW'}
        for i, attr in enumerate(attrs):
            if i in rm_list:
                continue

            # we handle some special cases here, if ever the attribute list contains RGB and separate R, G and B we
            # assume it's a color, if it's a double3 or float3 and we find the corresponding XYZ, we remove then to
            # avoid duplicates

            if attr.endswith('RGB'):
                if '%sR' % attr[:-3] in attrs:
                    attr.type = 'color'
                    for chan in 'RGB':
                        rm_list.add(attrs.index('%s%s' % (attr[:-3], chan)))

            # if the attribute's type isn't in the list, we remove
            elif attr.type not in MassAttribute_UI.ctx_icons:
                rm_list.add(i)

            elif attr.endswith('R'):
                if '%sG' % attr[:-1] in attrs and attr[:-1] in attrs:
                    attr.type = 'color'
                    for chan in 'RGB':
                        rm_list.add(attrs.index('%s%s' % (attr[:-1], chan)))

            elif attr.type in ('double3', 'double4', 'float3', 'float4'):
                if '%sX' % attr in attrs:
                    for chan in layers[attr.type[-1]]:
                        rm_list.add(attrs.index('%s%s' % (attr, chan)))

        # finally cleaning the list
        for i in sorted(rm_list, reverse=True):
            attrs.pop(i)

        return attrs

    def apply_value(self, value):
        """
        When the value is modified in the UI, we forward the given value and applies to the object's
        :param value: attribute's value, mixed type
        :type  value: mixed
        """
        # We get the only selected object in list and get it's super type (Shape, History or Object) and
        # type (float, int, string)
        item = self.WL_attributes.selectedItems()[0]
        attr = item.attribute
        shape = attr.super_type == Shape
        histo = attr.super_type == History
        tpe = item.attribute.type

        # eq dict for each context
        value = {
            'bool': bool,
            'int': int,
            'float': float,
            'enum': int,
            'str': str,
            'd3': list,
            'd4': list,
            'color': list
        }[self.ctx](value)

        # converting the selection into a set
        cmds.undoInfo(openChunk=True)
        targets = set(self.selection)

        # we propagate to children if 'Children' checkbox is on
        if self.WC_child.isChecked():
            for obj in list(targets):
                targets |= set(cmds.listRelatives(obj, ad=True))

        # if the target attribute is on the history, we add all selection's history to the list
        if histo:
            for obj in list(targets):
                targets.remove(obj)
                targets |= set(self.get_history(obj))

        # then we loop through target objects
        for obj in targets:
            # if the target is on the shape we get object's shape
            if shape and not histo:
                shapes = self.get_shapes(obj)

                if obj in shapes:
                    continue
                else:
                    obj = shapes[0]

            # then we try to apply depending on attribute's type
            try:
                correct_path = attr.path.replace(attr.obj, obj)

                if tpe == 'string':
                    cmds.setAttr(correct_path, value, type='string')

                elif tpe in ('double3', 'double4', 'float3', 'float4',
                             'color'):
                    cmds.setAttr(correct_path,
                                 *value,
                                 type='double%d' % len(value))

                else:
                    cmds.setAttr(correct_path, value)

            except RuntimeError:
                pass

        cmds.undoInfo(closeChunk=True)

    def update_setter(self):
        """
        When the list's selection changes we update the applier widget
        """
        item = self.WL_attributes.selectedItems()
        # abort if no item is selected
        if not len(item):
            return

        # getting attribute's parameter
        attr = item[0].attribute

        if len(self.selection):
            try:
                # looping until we find a context having the current attribute's type
                for applier in self.ctx_wide:
                    if attr.type in self.ctx_wide[applier]:
                        break
                # then we apply for the given path (obj.attribute)
                self.appliers[applier](attr.path)

                # and connecting event to the self.apply_value function
                self.applier.widget_event(self.ctx).connect(self.apply_value)

            # otherwise selection or type is invalid
            except IndexError:
                self.ctx = None

    def update_attributes(self, selection=None, *args):
        """
        Update the attributes for the given selection, looping through objects' attributes, finding attr in common
        between all objects then cleaning the lists, doing the same for shapes and / or histories
        :param selection: object's selection
        """
        # redefining lists as set to intersect union etc
        self.objs_attr = set()
        self.shps_attr = set()

        # pre init
        self.WL_attributes.clear()
        self.applier.unset_editors()

        self.selection = selection or (cmds.ls(
            sl=True) if self.WC_liveu.isChecked() else self.selection)

        self.WV_title.setText(self.format_title(self.selection))
        self.WV_title.setVisible(bool(len(self.selection)))
        self.WB_select.setVisible(bool(len(self.selection)))

        if not len(self.selection):
            return

        def get_usable_attrs(obj, super_type):
            """
            Small internal function to get a compatible attributes' list for the given object and assign the given
            super_type to it (Object, Shape or History)
            :param        obj: object's name
            :type         obj: str
            :param super_type: attribute's main type
            :type  super_type: Object | Shape | History
            :return:
            """
            return set([
                MassAttribute_UI.Attribute('%s.%s' % (obj, attr), super_type)
                for attr in cmds.listAttr(
                    obj, se=True, ro=False, m=True, w=True)
            ])

        if len(self.selection):
            self.objs_attr = get_usable_attrs(self.selection[0], Object)

            # if we also want the object's history we add it to the initial set
            if self.WC_histo.isChecked():
                for histo in self.get_history(self.selection[0]):
                    self.objs_attr |= get_usable_attrs(histo, History)

            # filling the shape's set
            for shape in (self.get_shapes(self.selection[0]) or []):
                self.shps_attr |= get_usable_attrs(shape, Shape)

            # if selection's length bigger than one we compare by intersection with the other sets
            if len(self.selection) > 1:
                for obj in self.selection:
                    sub_attr = get_usable_attrs(obj, Object)

                    if self.WC_histo.isChecked():
                        for histo in self.get_history(obj):
                            sub_attr |= get_usable_attrs(histo, History)

                    self.objs_attr.intersection_update(sub_attr)

                    for shape in (self.get_shapes(self.selection[0]) or []):
                        self.shps_attr.intersection_update(
                            get_usable_attrs(shape, Shape))

            # finally getting all intersecting attributes' types
            self.objs_attr = self.get_attributes_type(self.objs_attr)
            self.shps_attr = self.get_attributes_type(self.shps_attr)

        # and filtering the list
        self.filter()

    def add_set(self, iterable, title=None):
        """
        Adding the given iterable to the list with a first Separator object with given title
        :param iterable: list of item's attributes
        :param    title: Separator's name
        """
        if len(iterable):
            # if title is given we first add a Separator item to indicate coming list title
            if title:
                self.WL_attributes.addTopLevelItem(
                    QTreeWidget_Separator(title))

            items = []
            for attr in sorted(iterable):
                item = QTreeWidgetItem([attr])
                # assigning the attribute itself inside a custom parameter
                item.attribute = attr
                items.append(item)

            # finally adding all the items to the list
            self.WL_attributes.addTopLevelItems(items)

    def filter(self):
        """
        Filter the list with UI's parameters, such as name or type filtering, etc
        """
        # pre cleaning
        self.WL_attributes.clear()

        # using regex compile to avoid re execution over many attributes
        mask = self.WV_search.text()
        case = 0 if self.WC_cases.isChecked() else re.IGNORECASE
        re_start = re.compile(r'^%s.*?' % mask, case)
        re_cont = re.compile(r'.*?%s.*?' % mask, case)

        # getting the four different lists
        obj_start = set([at for at in self.objs_attr if re_start.search(at)])
        shp_start = set([at for at in self.shps_attr if re_start.search(at)])

        # if type filtering is one we only extract the wanted attribute's type
        if self.WC_types.isChecked():
            obj_start = set([
                at for at in obj_start if at.type in self.ctx_wide[
                    self.WL_attrtype.currentText().lower()]
            ])
            shp_start = set([
                at for at in shp_start if at.type in self.ctx_wide[
                    self.WL_attrtype.currentText().lower()]
            ])

        # finally adding the current sets if there is a mask we add the also the containing matches
        if mask:
            # getting contains filtering and type containers filtering
            obj_contains = obj_start.symmetric_difference(
                set([at for at in self.objs_attr if re_cont.search(at)]))
            shp_contains = shp_start.symmetric_difference(
                set([at for at in self.shps_attr if re_cont.search(at)]))
            if self.WC_types.isChecked():
                obj_contains = set([
                    at for at in obj_contains if at.type in self.ctx_wide[
                        self.WL_attrtype.currentText().lower()]
                ])
                shp_contains = set([
                    at for at in shp_contains if at.type in self.ctx_wide[
                        self.WL_attrtype.currentText().lower()]
                ])

            # adding the sets
            self.add_set(obj_start, 'Obj attributes starting with')
            self.add_set(obj_contains, 'Obj attributes containing')
            self.add_set(shp_start, 'Shape attributes starting with')
            self.add_set(shp_contains, 'Shape attributes containing')

        else:
            self.add_set(obj_start, 'Object\'s attributes')
            self.add_set(shp_start, 'Shape\'s attributes')

        # and we select the first one if ever there is something in the list
        if self.WL_attributes.topLevelItemCount():
            self.WL_attributes.setItemSelected(
                self.WL_attributes.topLevelItem(1), True)
Exemple #13
0
class HygieneReportPop(QDialog):
    """
    The Report Form Handler
    """
    def __init__(self, parent=None, code=None, table=None):
        super(HygieneReportPop, self).__init__()
        self.setup_pop()  # do not change the order of these two function
        self.code = code
        self.parent_object = parent
        self.image_data = None
        self.process_override(
            table=table)  # do not change the order of these two function
        if not code:
            self.update_button.setHidden(True)
            self.delete_button.setHidden(True)
            self.calculate_code()
        else:
            self.create_button.setHidden(True)
            self.update_data()

    def process_override(self, table):
        """
        Function that assigns the table and tryton backend handler objects based on the tables
        :param table: the table name
        """
        try:
            if table == 'pest_table':
                self.backend_handle = self.parent_object.pest
                self.table = self.parent_object.report_hyginepest_table
            elif table == 'water_table':
                self.backend_handle = self.parent_object.water
                self.table = self.parent_object.report_hyginewater_table
            elif table == 'health_table':
                self.backend_handle = self.parent_object.health
                self.table = self.parent_object.report_health_table
        except Exception:
            if settings.level == 10:
                logger.exception('raised exception')
            return False, 'Some Internal Error'

    def setup_pop(self):
        """
        sets up the form.
        """
        self.grid_layout = QGridLayout(self)
        self.label_1 = QLabel(self)
        self.grid_layout.addWidget(self.label_1, 0, 0, 1, 1)
        self.code_line = QLineEdit(self)
        self.code_line.setValidator(QIntValidator(0, 99999))
        self.grid_layout.addWidget(self.code_line, 0, 1, 1, 1)
        self.label_2 = QLabel(self)
        self.grid_layout.addWidget(self.label_2, 1, 0, 1, 1)
        self.date_line = QDateEdit(self)
        self.date_line.setCalendarPopup(True)
        self.grid_layout.addWidget(self.date_line, 1, 1, 1, 1)
        self.label_3 = QLabel(self)
        self.grid_layout.addWidget(self.label_3, 2, 0, 1, 1)
        self.organization_line = QLineEdit(self)
        self.grid_layout.addWidget(self.organization_line, 2, 1, 1, 1)
        self.label_4 = QLabel(self)
        self.grid_layout.addWidget(self.label_4, 3, 0, 1, 1)
        self.test_line = QLineEdit(self)
        self.grid_layout.addWidget(self.test_line, 3, 1, 1, 1)
        self.label_5 = QLabel(self)
        self.grid_layout.addWidget(self.label_5, 4, 0, 1, 1)
        self.description_line = QLineEdit(self)
        self.grid_layout.addWidget(self.description_line, 4, 1, 1, 1)
        self.image_label = QLabel(self)
        self.pixmap = QPixmap(':/images/upload.png')
        # self.pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio, Qt.FastTransformation) #not used
        self.image_label.setPixmap(self.pixmap)
        self.image_label.setScaledContents(True)
        self.grid_layout.addWidget(self.image_label, 0, 2, 5, 2)
        self.horizontal = QHBoxLayout()
        self.delete_button = QPushButton(self)
        self.horizontal.addWidget(self.delete_button)
        self.create_button = QPushButton(self)
        self.horizontal.addWidget(self.create_button)
        self.update_button = QPushButton(self)
        self.horizontal.addWidget(self.update_button)
        self.upload_button = QPushButton(self)
        self.horizontal.addWidget(self.upload_button)
        self.grid_layout.addLayout(self.horizontal, 5, 0, 1, 4)
        ### retanslate
        self.label_1.setText(
            QApplication.translate("MainWindow", "Code", None,
                                   QApplication.UnicodeUTF8))
        self.label_2.setText(
            QApplication.translate("MainWindow", "Date", None,
                                   QApplication.UnicodeUTF8))
        self.date_line.setDisplayFormat(
            QApplication.translate("MainWindow", "dd/MM/yyyy", None,
                                   QApplication.UnicodeUTF8))
        self.label_3.setText(
            QApplication.translate("MainWindow", "Organization Name", None,
                                   QApplication.UnicodeUTF8))
        self.label_4.setText(
            QApplication.translate("MainWindow", "Test", None,
                                   QApplication.UnicodeUTF8))
        self.label_5.setText(
            QApplication.translate("MainWindow", "Description", None,
                                   QApplication.UnicodeUTF8))
        self.delete_button.setText(
            QApplication.translate("MainWindow", "Delete", None,
                                   QApplication.UnicodeUTF8))
        self.create_button.setText(
            QApplication.translate("MainWindow", "Create", None,
                                   QApplication.UnicodeUTF8))
        self.update_button.setText(
            QApplication.translate("MainWindow", "Update", None,
                                   QApplication.UnicodeUTF8))
        self.upload_button.setText(
            QApplication.translate("MainWindow", "Upload", None,
                                   QApplication.UnicodeUTF8))
        self.create_button.clicked.connect(self.create_report)
        self.upload_button.clicked.connect(self.open_file)
        self.update_button.clicked.connect(self.update_report)
        self.delete_button.clicked.connect(self.delete_report)
        self.image_label.mouseReleaseEvent = self.image_viewer

    def update_data(self):
        """
        Updates the data in the form
        """
        try:
            data = self.backend_handle.read_report(code=int(self.code))
            if data:
                if data[0]:
                    data = data[1]
                    self.code_line.setText(data['code'])
                    self.code_line.setDisabled(True)
                    self.date_line.setDate(data['date'])
                    self.organization_line.setText(data['organization'])
                    self.test_line.setText(data['test'])
                    self.description_line.setText(data['description'])
                    self.image_data = str(data['report'])
                    self.pixmap = QPixmap()
                    self.pixmap.loadFromData(self.image_data)
                    self.image_label.setPixmap(
                        self.pixmap.scaled(self.image_label.size(),
                                           Qt.KeepAspectRatio,
                                           Qt.FastTransformation))
        except Exception:
            if settings.level == 10:
                logger.exception('raised exception')
            return False, 'Some Internal Error'

    def image_viewer(self, event=None):
        """pops up a image viewer to check the details"""
        dialog = QDialog()
        dialog.setWindowFlags(Qt.WindowTitleHint | Qt.WindowStaysOnTopHint)
        layout = QHBoxLayout(dialog)
        label = QLabel(dialog)
        # self.pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio, Qt.FastTransformation)
        label.setPixmap(self.pixmap)
        label.setScaledContents(True)
        layout.addWidget(label)
        dialog.exec_()

    def open_file(self):
        """
        saves the file
        """
        dialog = QFileDialog(self)
        dialog.setNameFilter("Image Files (*.png *.jpg *.bmp)")
        dialog.setStyleSheet('color:black;')
        name = ''
        if dialog.exec_():
            name = dialog.selectedFiles()
        if name:
            self.image_data = open(name[0], 'rb').read()
            self.pixmap = QPixmap(name[0])
        self.image_label.setPixmap(
            self.pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio,
                               Qt.FastTransformation))

    def create_report(self):
        """
        Creates a new Report
        """
        try:
            data = self.get_data()
            if data:
                status = self.backend_handle.create_report(data=data)
                if status:
                    if status[0]:
                        QMessageBox.information(self, 'Success', status[1],
                                                QMessageBox.Ok)
                        self.create_button.setHidden(True)
                        self.delete_button.setVisible(True)
                        self.update_button.setVisible(True)
                        self.code = data['code']
                        self.update_data()
                    else:
                        QMessageBox.critical(self, 'Error', status[1],
                                             QMessageBox.Ok)
        except Exception:
            if settings.level == 10:
                logger.exception('raised exception')
            return False, 'Some Internal Error'

    def update_report(self):
        """
        Updates an existing Report
        """
        try:
            data = self.get_data()
            if data:
                status = self.backend_handle.update_report(data=data)
                if status:
                    if status[0]:
                        QMessageBox.information(self, 'Success', status[1],
                                                QMessageBox.Ok)
                    else:
                        QMessageBox.critical(self, 'Error', status[1],
                                             QMessageBox.Ok)
        except Exception:
            if settings.level == 10:
                logger.exception('raised exception')
            return False, 'Some Internal Error'

    def get_data(self):
        """
        Gets the data from the form as a dictionary
        :return:dictionary
        """
        try:
            data = {}
            data['code'] = self.code_line.text()
            data['date'] = self.date_line.text()
            data['organization'] = self.organization_line.text()
            data['test'] = self.test_line.text()
            data['description'] = self.description_line.text()
            data['report'] = self.image_data
            for key, value in data.iteritems():
                if not value:
                    QMessageBox.critical(
                        self, 'Error',
                        'Insert Proper value for %s' % key.title(),
                        QMessageBox.Ok)
                    return False
            return data
        except Exception:
            if settings.level == 10:
                logger.exception('raised exception')
            return False

    def delete_report(self):
        """
        Deletes the existing report
        :return:
        """
        try:
            code = self.code_line.text()
            status = self.backend_handle.delete_report(code=code)
            if status:
                if status[0]:
                    QMessageBox.information(self, 'Success', status[1],
                                            QMessageBox.Ok)
                    self.close()
                else:
                    QMessageBox.critical(self, 'Error', status[1],
                                         QMessageBox.Ok)
        except Exception:
            if settings.level == 10:
                logger.exception('raised exception')
            return False, 'Some Internal Error'

    def calculate_code(self):
        """
        Calculates the entry number, Eases the user for entering the code
        """
        try:
            table = self.table
            if table:
                code = []
                rows = table.rowCount()
                for row in range(rows):
                    code.append(table.item(row, 0).text())
                if code:
                    new_code = str(int(max(code)) + 1)
                    self.code_line.setText(new_code)
        except Exception:
            if settings.level == 10:
                logger.exception('raised exception')
            return False, 'Some Internal Error'
Exemple #14
0
class MassAttribute_UI(QDialog):
    """
    The main UI
    """
    class Applikator(QObject):
        """
        This is the core applier which toggle the display of the corresponding widget and handling events' connections
        """
        def __init__(self, parent=None):
            super(MassAttribute_UI.Applikator, self).__init__()
            self.root = parent

        def widget_event(self, t):
            """
            Return the correct widget's event depending on attribute's type
            :param t: the attribute's type
            :type  t: str
            :return: the event
            :rtype : Signal
            """
            return {'float': self.root.W_EDI_float.valueChanged, 'enum': self.root.W_EDI_enum.currentIndexChanged,
                    'int': self.root.W_EDI_int.valueChanged, 'bool': self.root.W_EDI_bool.stateChanged,
                    'str': self.root.W_EDI_str.textChanged, 'd3': self.root.W_EDI_d3.valuesChanged,
                    'd4': self.root.W_EDI_d4.valuesChanged, 'color': self.root.W_EDI_color.colorChanged}[t]

        def unset_editors(self):
            """
            Toggle off all editors and disconnect the current one
            """
            for widget in (self.root.W_EDI_float, self.root.W_EDI_int, self.root.W_EDI_enum,
                           self.root.W_EDI_bool, self.root.W_EDI_str, self.root.W_EDI_d3,
                           self.root.W_EDI_d4, self.root.W_EDI_color):
                widget.setVisible(False)

            # trying to force disconnection
            try:
                self.widget_event(self.root.ctx).disconnect(self.root.apply_value)
            except (KeyError, RuntimeError):
                pass

        def prepare(applier_name):
            """
            A decorator to prepare the attribute depending on type for the corresponding widget and getting the
            attribute's value
            :param applier_name: attribute's type
            :type  applier_name: str
            """
            def sub_wrapper(func):
                def wrapper(self, attr_path):
                    self.unset_editors()
                    self.root.ctx = applier_name
                    self.root.__getattribute__('W_EDI_%s' % applier_name).setVisible(True)
                    ret = func(self, cmds.getAttr(attr_path), attr_path)
                    return ret
                return wrapper
            return sub_wrapper

        @staticmethod
        def get_bounds(obj, attr, min_default, max_default):
            """
            Try to retrieve the range for the given attribute, if min or max fail it'll set default values
            :param         obj: the object's name
            :type          obj: str
            :param        attr: attribute's name
            :type         attr: str
            :param min_default: minimum default value
            :param max_default: max default value
            :type  min_default: float | int
            :type  max_default: float | int
            :return: minimum, maximum
            :rtype : tuple
            """
            try:
                assert cmds.attributeQuery(attr, n=obj, mxe=True)
                maxi = cmds.attributeQuery(attr, n=obj, max=True)[0]
            except (RuntimeError, AssertionError):
                maxi = max_default
            try:
                assert cmds.attributeQuery(attr, n=obj, mne=True)
                mini = cmds.attributeQuery(attr, n=obj, min=True)[0]
            except (RuntimeError, AssertionError):
                mini = min_default
            return mini, maxi

        @prepare('float')
        def apply_float(self, value, path):
            """
            Float attribute case
            :param value: attribute's value
            :param  path: attribute's path = obj.attr
            """
            obj, attr = path.split('.', 1)
            self.root.W_EDI_float.setRange(*self.get_bounds(obj, attr, -100.0, 100.0))
            self.root.W_EDI_float.setValue(value)

        @prepare('enum')
        def apply_enum(self, value, path):
            """Enum case"""
            self.root.W_EDI_enum.clear()
            obj, attr = path.split('.', 1)
            try:
                enums = [enum.split('=')[0] for enum in cmds.attributeQuery(attr, n=obj, listEnum=True)[0].split(':')]
            except RuntimeError:
                self.apply_int(path)
            else:
                self.root.W_EDI_enum.addItems(enums)
                self.root.W_EDI_enum.setCurrentIndex(enums.index(cmds.getAttr(path, asString=True)))

        @prepare('int')
        def apply_int(self, value, path):
            """Integer case"""
            obj, attr = path.split('.', 1)
            self.root.W_EDI_int.setRange(*self.get_bounds(obj, attr, -1000, 1000))
            self.root.W_EDI_int.setValue(value)

        @prepare('bool')
        def apply_bool(self, value, path):
            """Boolean case"""
            self.root.W_EDI_bool.setChecked(value)
            self.root.W_EDI_bool.setText(path.split('.', 1)[1])

        @prepare('str')
        def apply_str(self, value, path):
            """String case"""
            self.root.W_EDI_str.setText(value)

        @prepare('d3')
        def apply_d3(self, value, path):
            """3D array case"""
            self.root.W_EDI_d3.setValues(value[0])

        @prepare('d4')
        def apply_d4(self, value, path):
            """4D array case"""
            self.root.W_EDI_d4.setValues(value[0])

        @prepare('color')
        def apply_color(self, value, path):
            """Color case"""
            try:
                colors = value[0]
                self.root.W_EDI_color.setColor([int(c * 255) for c in colors])
            except TypeError:
                self.apply_int(value, path)

    class Attribute(str):
        """
        A custom string attribute class to ship more information into the string variable
        """
        def __new__(cls, path='', super_type=Object):
            obj, attr = path.split('.', 1)

            str_obj = str.__new__(cls, attr)

            str_obj.obj, str_obj.attr = obj, attr
            str_obj.path = path
            str_obj.super_type = super_type
            str_obj.type = None

            return str_obj

    # static variables to pre-load icons and attributes short names
    ctx_icons = {'float': QIcon(':render_decomposeMatrix.png'),
                 'enum': QIcon(':showLineNumbers.png'),
                 'bool': QIcon(':out_decomposeMatrix.png'),
                 'time': QIcon(':time.svg'),
                 'byte': QIcon(':out_defaultTextureList.png'),
                 'angle': QIcon(':angleDim.png'),
                 'string': QIcon(':text.png'),
                 'float3': QIcon(':animCurveTA.svg'),
                 'float4': QIcon(':animCurveTA.svg'),
                 'color': QIcon(':clampColors.svg')}

    for ctx in ('doubleLinear', 'double', 'long', 'short'):
        ctx_icons[ctx] = ctx_icons['float']

    ctx_icons['double3'] = ctx_icons['float3']
    ctx_icons['double4'] = ctx_icons['float4']

    ctx_wide = {'float': ('float', 'doubleLinear', 'double', 'long', 'short'),
                'enum': ('enum',),
                'bool': ('bool',),
                'time': ('time',),
                'byte': ('byte',),
                'angle': ('doubleAngle',),
                'string': ('string',),
                'float3': ('double3', 'float3'),
                'float4': ('double4', 'float4'),
                'color': ('color',)}

    def __init__(self, parent=None):
        super(MassAttribute_UI, self).__init__(parent)
        # Abstract
        self.applier = self.Applikator(self)
        self.selection = []
        self.callback = None
        self.ctx = None
        # storing found attributes' types to avoid double check
        self.solved = {}
        self.setLocale(QLocale.C)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.setAttribute(Qt.WA_QuitOnClose)

        self.setFixedWidth(300)
        self.setWindowTitle('Massive Attribute Modifier')

        # UI
        L_main = QVBoxLayout()

        self.WV_title = QLabel('')
        self.WV_title.setVisible(False)
        self.WV_title.setFont(QFont('Verdana', 10))
        self.WV_title.setContentsMargins(0, 0, 0, 7)

        self.WB_select = QPushButton('Select')
        self.WB_select.setVisible(False)
        self.WB_select.setFixedWidth(50)
        self.WB_select.clicked.connect(lambda: cmds.select(self.selection))

        self.WB_update = QPushButton('Update')
        self.WB_update.setFixedWidth(50)
        self.WB_update.clicked.connect(lambda:self.update_attributes(cmds.ls(sl=True)))

        self.WV_search = Filter()
        self.WV_search.textChanged.connect(self.filter)

        self.WC_cases = QCheckBox('Case sensitive')
        self.WC_cases.stateChanged.connect(self.filter)

        self.WC_types = QCheckBox('Type filtering')

        self.WL_attrtype = QComboBox()
        self.WL_attrtype.setEnabled(False)

        for i, ctx in enumerate(sorted(self.ctx_wide)):
            self.WL_attrtype.addItem(ctx.title())
            self.WL_attrtype.setItemIcon(i, self.ctx_icons[ctx])

        L_attrtype = line(self.WC_types, self.WL_attrtype)

        self.WC_types.stateChanged.connect(partial(self.update_attributes, self.selection))
        self.WC_types.stateChanged.connect(self.WL_attrtype.setEnabled)
        self.WL_attrtype.currentIndexChanged.connect(self.filter)

        self.WC_liveu = QCheckBox('Live')
        self.WC_liveu.stateChanged.connect(self.WB_update.setDisabled)
        self.WC_liveu.stateChanged.connect(self.set_callback)

        self.WC_histo = QCheckBox('Load history')
        self.WC_histo.setChecked(True)
        self.WC_histo.stateChanged.connect(partial(self.update_attributes, self.selection))

        self.WC_child = QCheckBox('Children')
        self.WC_child.stateChanged.connect(partial(self.update_attributes, self.selection))

        options = group('Options', line(self.WC_cases, L_attrtype),
                        line(self.WC_child, self.WC_histo, self.WC_liveu, self.WB_update))
        options.layout().setSpacing(2)

        self.WL_attributes = QTreeWidget()
        self.WL_attributes.setStyleSheet('QTreeView {alternate-background-color: #1b1b1b;}')
        self.WL_attributes.setAlternatingRowColors(True)
        self.WL_attributes.setHeaderHidden(True)
        self.WL_attributes.setRootIsDecorated(False)

        self.objs_attr = set()
        self.shps_attr = set()

        self.W_EDI_float = FloatBox()
        self.W_EDI_int = IntBox()
        self.W_EDI_enum = QComboBox()
        self.W_EDI_bool = QCheckBox()
        self.W_EDI_str = QLineEdit()
        self.W_EDI_d3 = Double3()
        self.W_EDI_d4 = Double4()
        self.W_EDI_color = ColorPicker()

        # Final layout
        L_title = line(self.WV_title, self.WB_select)
        L_title.setStretch(0, 1)
        L_main.addLayout(L_title)
        L_main.setAlignment(Qt.AlignLeft)
        L_main.addWidget(self.WV_search)
        L_main.addWidget(options)
        L_main.addWidget(self.WL_attributes)
        L_edits = col(self.W_EDI_bool, self.W_EDI_int, self.W_EDI_float,
                      self.W_EDI_enum, self.W_EDI_str, self.W_EDI_d3, self.W_EDI_d4,
                      self.W_EDI_color)
        L_edits.setContentsMargins(0, 8, 0, 0)
        L_main.addLayout(L_edits)
        L_main.setStretch(3, 1)
        L_main.setSpacing(2)

        self.appliers = {'float': self.applier.apply_float,
                         'enum': self.applier.apply_enum,
                         'bool': self.applier.apply_bool,
                         'time': self.applier.apply_float,
                         'byte': self.applier.apply_int,
                         'angle': self.applier.apply_float,
                         'string': self.applier.apply_str,
                         'float3': self.applier.apply_d3,
                         'float4': self.applier.apply_d4,
                         'color': self.applier.apply_color}

        self.setLayout(L_main)

        # final settings
        self.WL_attributes.itemSelectionChanged.connect(self.update_setter)
        self.applier.unset_editors()

    def closeEvent(self, *args, **kwargs):
        self.set_callback(False)

    def set_callback(self, state):
        """
        Toggle selection event callback
        :param state: checkbox's state
        :type  state: bool | int
        """
        if state and not self.callback:
            self.callback = MEventMessage.addEventCallback('SelectionChanged', self.update_attributes)
            self.update_attributes(cmds.ls(sl=True))

        elif not state and self.callback:
            MMessage.removeCallback(self.callback)
            self.callback = None

    @staticmethod
    def format_title(nodes):
        """
        Extract the matching characters from a given nodes selection, if begin matches it will return "joint*" with a
        wildcard when names don't match
        :param nodes: objects' list
        :type  nodes: list | tuple
        :return: the formatted name with the corresponding characters
        :rtype : str
        """
        res = None

        if nodes:
            # we get the first node as a reference
            node = nodes[0]
            # and compare with the other nodes
            subs = [w for w in nodes if w != node]

            l = 1
            valid = True
            # will continue until l (length) match the full name's length or until names don't match
            while l < len(node) and valid:
                for sub in subs:
                    if not sub.startswith(node[:l]):
                        valid = False
                        break

                else:
                    l += 1

            # if matching characters isn't long enough we only display the number of nodes selected
            if l <= 3:
                res = '%i objects' % len(nodes)

            # otherwise showing matching pattern
            elif l < len(node) or len(nodes) > 1:
                res = node[:l - 1] + '* (%i objects)' % len(nodes)

            else:
                res = node

        return res

    @staticmethod
    def get_history(node):
        """
        Extract history for the given node
        :rtype: list
        """
        return cmds.listHistory(node, il=2, pdo=True) or []

    @staticmethod
    def get_shapes(node):
        """
        Extract shape(s) for the given node
        :rtype: list
        """
        return cmds.listRelatives(node, s=True, ni=True, f=True)

    def get_attributes_type(self, attrs):
        """
        For a given list of attributes of type Attribute, will loop through and fill the type parameter of the
         attribute with the corresponding type, if type is invalid or not handled, it'll remove it
        :param attrs: attributes' list
        :type  attrs: [MassAttribute_UI.Attribute]
        :return: cleaned and filled attributes' list
        :rtype: [MassAttribute_UI.Attribute]
        """
        attrs = list(attrs)
        # first we sort the attributes' list
        attrs.sort()

        # then we try to extract the attribute's type
        for i, attr in enumerate(attrs):
            try:
                if attr.attr in self.solved:
                    attr.type = self.solved[attr.attr]
                    raise RuntimeError
                tpe = cmds.getAttr(attr.path, typ=True)
                assert tpe
                attr.type = tpe
                self.solved[attr.attr] = tpe
            except (AssertionError, ValueError, RuntimeError):
                pass

        # defining a to-remove list
        rm_list = set()

        layers = {'3': 'XYZ', '4': 'XYZW'}
        for i, attr in enumerate(attrs):
            if i in rm_list:
                continue

            # we handle some special cases here, if ever the attribute list contains RGB and separate R, G and B we
            # assume it's a color, if it's a double3 or float3 and we find the corresponding XYZ, we remove then to
            # avoid duplicates

            if attr.endswith('RGB'):
                if '%sR' % attr[:-3] in attrs:
                    attr.type = 'color'
                    for chan in 'RGB':
                        rm_list.add(attrs.index('%s%s' % (attr[:-3], chan)))

            # if the attribute's type isn't in the list, we remove
            elif attr.type not in MassAttribute_UI.ctx_icons:
                rm_list.add(i)

            elif attr.endswith('R'):
                if '%sG' % attr[:-1] in attrs and attr[:-1] in attrs:
                    attr.type = 'color'
                    for chan in 'RGB':
                        rm_list.add(attrs.index('%s%s' % (attr[:-1], chan)))

            elif attr.type in ('double3', 'double4', 'float3', 'float4'):
                if '%sX' % attr in attrs:
                    for chan in layers[attr.type[-1]]:
                        rm_list.add(attrs.index('%s%s' % (attr, chan)))

        # finally cleaning the list
        for i in sorted(rm_list, reverse=True):
            attrs.pop(i)

        return attrs

    def apply_value(self, value):
        """
        When the value is modified in the UI, we forward the given value and applies to the object's
        :param value: attribute's value, mixed type
        :type  value: mixed
        """
        # We get the only selected object in list and get it's super type (Shape, History or Object) and
        # type (float, int, string)
        item = self.WL_attributes.selectedItems()[0]
        attr = item.attribute
        shape = attr.super_type == Shape
        histo = attr.super_type == History
        tpe = item.attribute.type

        # eq dict for each context
        value = {'bool': bool,
                 'int': int,
                 'float': float,
                 'enum': int,
                 'str': str,
                 'd3': list,
                 'd4': list,
                 'color': list}[self.ctx](value)

        # converting the selection into a set
        cmds.undoInfo(openChunk=True)
        targets = set(self.selection)

        # we propagate to children if 'Children' checkbox is on
        if self.WC_child.isChecked():
            for obj in list(targets):
                targets |= set(cmds.listRelatives(obj, ad=True))

        # if the target attribute is on the history, we add all selection's history to the list
        if histo:
            for obj in list(targets):
                targets.remove(obj)
                targets |= set(self.get_history(obj))

        # then we loop through target objects
        for obj in targets:
            # if the target is on the shape we get object's shape
            if shape and not histo:
                shapes = self.get_shapes(obj)

                if obj in shapes:
                    continue
                else:
                    obj = shapes[0]

            # then we try to apply depending on attribute's type
            try:
                correct_path = attr.path.replace(attr.obj, obj)

                if tpe == 'string':
                    cmds.setAttr(correct_path, value, type='string')

                elif tpe in ('double3', 'double4', 'float3', 'float4', 'color'):
                    cmds.setAttr(correct_path, *value, type='double%d' % len(value))

                else:
                    cmds.setAttr(correct_path, value)

            except RuntimeError:
                pass

        cmds.undoInfo(closeChunk=True)

    def update_setter(self):
        """
        When the list's selection changes we update the applier widget
        """
        item = self.WL_attributes.selectedItems()
        # abort if no item is selected
        if not len(item):
            return

        # getting attribute's parameter
        attr = item[0].attribute

        if len(self.selection):
            try:
                # looping until we find a context having the current attribute's type
                for applier in self.ctx_wide:
                    if attr.type in self.ctx_wide[applier]:
                        break
                # then we apply for the given path (obj.attribute)
                self.appliers[applier](attr.path)

                # and connecting event to the self.apply_value function
                self.applier.widget_event(self.ctx).connect(self.apply_value)

            # otherwise selection or type is invalid
            except IndexError:
                self.ctx = None

    def update_attributes(self, selection=None, *args):
        """
        Update the attributes for the given selection, looping through objects' attributes, finding attr in common
        between all objects then cleaning the lists, doing the same for shapes and / or histories
        :param selection: object's selection
        """
        # redefining lists as set to intersect union etc
        self.objs_attr = set()
        self.shps_attr = set()

        # pre init
        self.WL_attributes.clear()
        self.applier.unset_editors()

        self.selection = selection or (cmds.ls(sl=True) if self.WC_liveu.isChecked() else self.selection)

        self.WV_title.setText(self.format_title(self.selection))
        self.WV_title.setVisible(bool(len(self.selection)))
        self.WB_select.setVisible(bool(len(self.selection)))

        if not len(self.selection):
            return

        def get_usable_attrs(obj, super_type):
            """
            Small internal function to get a compatible attributes' list for the given object and assign the given
            super_type to it (Object, Shape or History)
            :param        obj: object's name
            :type         obj: str
            :param super_type: attribute's main type
            :type  super_type: Object | Shape | History
            :return:
            """
            return set([MassAttribute_UI.Attribute('%s.%s' % (obj, attr), super_type) for attr in
                        cmds.listAttr(obj, se=True, ro=False, m=True, w=True)])

        if len(self.selection):
            self.objs_attr = get_usable_attrs(self.selection[0], Object)

            # if we also want the object's history we add it to the initial set
            if self.WC_histo.isChecked():
                for histo in self.get_history(self.selection[0]):
                    self.objs_attr |= get_usable_attrs(histo, History)

            # filling the shape's set
            for shape in (self.get_shapes(self.selection[0]) or []):
                self.shps_attr |= get_usable_attrs(shape, Shape)

            # if selection's length bigger than one we compare by intersection with the other sets
            if len(self.selection) > 1:
                for obj in self.selection:
                    sub_attr = get_usable_attrs(obj, Object)

                    if self.WC_histo.isChecked():
                        for histo in self.get_history(obj):
                            sub_attr |= get_usable_attrs(histo, History)

                    self.objs_attr.intersection_update(sub_attr)

                    for shape in (self.get_shapes(self.selection[0]) or []):
                        self.shps_attr.intersection_update(get_usable_attrs(shape, Shape))

            # finally getting all intersecting attributes' types
            self.objs_attr = self.get_attributes_type(self.objs_attr)
            self.shps_attr = self.get_attributes_type(self.shps_attr)

        # and filtering the list
        self.filter()

    def add_set(self, iterable, title=None):
        """
        Adding the given iterable to the list with a first Separator object with given title
        :param iterable: list of item's attributes
        :param    title: Separator's name
        """
        if len(iterable):
            # if title is given we first add a Separator item to indicate coming list title
            if title:
                self.WL_attributes.addTopLevelItem(QTreeWidget_Separator(title))

            items = []
            for attr in sorted(iterable):
                item = QTreeWidgetItem([attr])
                # assigning the attribute itself inside a custom parameter
                item.attribute = attr
                items.append(item)

            # finally adding all the items to the list
            self.WL_attributes.addTopLevelItems(items)

    def filter(self):
        """
        Filter the list with UI's parameters, such as name or type filtering, etc
        """
        # pre cleaning
        self.WL_attributes.clear()

        # using regex compile to avoid re execution over many attributes
        mask = self.WV_search.text()
        case = 0 if self.WC_cases.isChecked() else re.IGNORECASE
        re_start = re.compile(r'^%s.*?' % mask, case)
        re_cont = re.compile(r'.*?%s.*?' % mask, case)

        # getting the four different lists
        obj_start = set([at for at in self.objs_attr if re_start.search(at)])
        shp_start = set([at for at in self.shps_attr if re_start.search(at)])

        # if type filtering is one we only extract the wanted attribute's type
        if self.WC_types.isChecked():
            obj_start = set([at for at in obj_start if
                             at.type in self.ctx_wide[self.WL_attrtype.currentText().lower()]])
            shp_start = set([at for at in shp_start if
                             at.type in self.ctx_wide[self.WL_attrtype.currentText().lower()]])

        # finally adding the current sets if there is a mask we add the also the containing matches
        if mask:
            # getting contains filtering and type containers filtering
            obj_contains = obj_start.symmetric_difference(set([at for at in self.objs_attr if re_cont.search(at)]))
            shp_contains = shp_start.symmetric_difference(set([at for at in self.shps_attr if re_cont.search(at)]))
            if self.WC_types.isChecked():
                obj_contains = set([at for at in obj_contains if
                                    at.type in self.ctx_wide[self.WL_attrtype.currentText().lower()]])
                shp_contains = set([at for at in shp_contains if
                                    at.type in self.ctx_wide[self.WL_attrtype.currentText().lower()]])

            # adding the sets
            self.add_set(obj_start, 'Obj attributes starting with')
            self.add_set(obj_contains, 'Obj attributes containing')
            self.add_set(shp_start, 'Shape attributes starting with')
            self.add_set(shp_contains, 'Shape attributes containing')

        else:
            self.add_set(obj_start, 'Object\'s attributes')
            self.add_set(shp_start, 'Shape\'s attributes')

        # and we select the first one if ever there is something in the list
        if self.WL_attributes.topLevelItemCount():
            self.WL_attributes.setItemSelected(self.WL_attributes.topLevelItem(1), True)
class LabelImage(QLabel):
    _pixmap = None
    _image = None
    _hide_icons = False
    _icon_size = 28

    download_started = Signal()
    download_ended = Signal()
    button_download_clicked = Signal()

    def __init__(self, parent=None):
        QLabel.__init__(self, parent)
        self.setText(self.tr("<u>can't obtain picture</u>"))

        self.lblLoadProcess = QLabel("...", self)
        self.lblLoadProcess.setMovie(QMovie(":/loading.gif"))
        self.lblLoadProcess.setAlignment(Qt.AlignCenter)
        self.lblLoadProcess.hide()

        self.btnDownload = QPushButton("", self)
        self.btnDownload.setIcon(QIcon(":/folder.png"))
        self.btnDownload.setIconSize(QSize(self._icon_size, self._icon_size))
        self.btnDownload.setFlat(True)
        self.btnDownload.clicked.connect(self.save_image)
        self.btnDownload.clicked.connect(self.button_download_clicked)

        self.download_ended.connect(self.check_image)
        self.download_ended.connect(self.lblLoadProcess.hide)
        self.download_ended.connect(self.btnDownload.show)
        self.download_ended.connect(self.end_load_anim)

        self.download_started.connect(self.start_load_anim)
        self.download_started.connect(self.btnDownload.hide)
        self.download_started.connect(self.lblLoadProcess.show)
        # self.destroyed.connect(self.on_destroyed)

    def start_load_anim(self):
        self.lblLoadProcess.movie().start()

    def end_load_anim(self):
        self.lblLoadProcess.movie().stop()

    @property
    def save_path(self):
        return os.path.join(config.save_dir, self._image.celeb.full_name, self._image.name)

    def save_image_thread_callback(self):
        self.download_started.emit()

        b = self._image.full_image_bytes
        path = self.save_path
        if not os.path.exists(os.path.dirname(path)):
            os.makedirs(os.path.dirname(path))

        img = Image.open(BytesIO(b))
        img.save(self.save_path)

        # f = open(path, 'w')
        # f.write(b)
        # f.close()
        try:
            self.download_ended.emit()
        except RuntimeError as e:
            log.debug(sys.exc_traceback)
            log.debug(sys.exc_info())

    def save_image(self):
        thread = Thread(target=self.save_image_thread_callback)
        thread.start()

    def check_image(self):
        if self._image:
            if os.path.exists(self.save_path):
                self.btnDownload.setIcon(QIcon(":/check.png"))
                return True
        self.btnDownload.setIcon(QIcon(":/folder.png"))
        return False

    def __setProp(self):
        if self._pixmap:
            QLabel.setPixmap(self, self._pixmap.scaled(
                self.width(), self.height(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
        else:
            QLabel.setPixmap(self, QPixmap())

    @property
    def image(self):
        return self._image

    @image.setter
    def image(self, image):
        assert isinstance(image, ThePlaceImage)
        self._image = image
        path = os.path.abspath(image.icon_cache_path).replace('\\', '/')
        self.check_image()
        self.setPixmap(QPixmap(path))

    def setPixmap(self, px):
        self._pixmap = px
        self.__setProp()
        self.btnDownload.setVisible(not self.hide_icons and px is not None)

    def resizeEvent(self, *args, **kwargs):
        super(LabelImage, self).resizeEvent(*args, **kwargs)
        self.__setProp()

        self.btnDownload.move(self.width() - self.btnDownload.width() - 3,
                              self.height() - self.btnDownload.height() - 2)
        self.lblLoadProcess.setGeometry(self.btnDownload.geometry())

    @property
    def hide_icons(self):
        return self._hide_icons

    @hide_icons.setter
    def hide_icons(self, value):
        self._hide_icons = value
        self.btnDownload.setVisible(not self.hide_icons)
Exemple #16
0
class BookAddForm(QScrollArea, Ui_BookForm):
    """ Interface for book input """

    column = {
        "barcode": 1,
        "title": 2,
        "author": 3,
        "s_author": 4,
        "publisher": 5,
        "year": 6,
        "price": 7,
        "description": 8,
        "stock": 9,
        "image": 10,
        "availability": 11,
    }

    IMG_SIZE = (150, 150)

    def __init__(self, parent=None):
        super(BookAddForm, self).__init__(parent)
        self.setupUi(self)
        # had to subclass this spinbox to support return grabbing
        self.edYear = ReturnKeySpinBox(self)
        self.edYearHolder.addWidget(self.edYear)
        # configuring id's for radio group
        self.radioAvailability.setId(self.rdSell, 0)
        self.radioAvailability.setId(self.rdRent, 1)
        self.radioAvailability.setId(self.rdInactive, 2)

        self._access = statics.access_level
        # for currency formatting
        self._locale = QLocale()

        # had to hardcode these, wouldn't work otherwise:
        self.contentsLayout.setAlignment(self.groupBox, QtCore.Qt.AlignTop)
        self.contentsLayout.setAlignment(self.groupBox_2, QtCore.Qt.AlignTop)

        self.log = logging.getLogger("BookForm")

        self.setup_model()
        self.setup_fields()

        self._subject_list = []

        # flag to indicate whether there were changes to the fields
        self._dirty = False
        # for use in selection docks, indicates a saved record
        self._book_id = None
        # user did input an image
        self._image_set = False

        # overlaying a clean button over the image (couldn't do it in designer)
        self.btnCleanImage = QPushButton()
        self.btnCleanImage.setIcon(QIcon(":icons/clean.png"))
        self.btnCleanImage.setFixedWidth(35)
        self.btnCleanImage.clicked.connect(self.clear_image)
        self.btnCleanImage.setVisible(False)
        clean_img_layout = QVBoxLayout(self.edImage)
        clean_img_layout.addWidget(self.btnCleanImage)
        clean_img_layout.setAlignment(Qt.AlignTop | Qt.AlignLeft)
        clean_img_layout.setContentsMargins(2, 2, 0, 0)

    def is_dirty(self):
        return self._dirty

    def setup_model(self):
        db = Db_Instance("form_book").get_instance()
        if not db.open():
            self.log.error(db.lastError().text())
            message = unicode("Erro de conexão\n\n" "Banco de dados indisponível".decode("utf-8"))
            QMessageBox.critical(self, "Seareiros - Cadastro de Livro", message)
        else:
            # book
            self._model = QSqlTableModel(self, db=db)
            self._model.setTable("book")
            # subject
            self._subject_model = QSqlTableModel(self, db=db)
            self._subject_model.setTable("subject")
            self._subject_model.select()
            # author
            self._author_model = QSqlTableModel(self, db=db)
            self._author_model.setTable("author")
            self._author_model.select()
            # sauthor
            self._s_author_model = QSqlTableModel(self, db=db)
            self._s_author_model.setTable("s_author")
            self._s_author_model.select()
            # publisher
            self._publisher_model = QSqlTableModel(self, db=db)
            self._publisher_model.setTable("publisher")
            self._publisher_model.select()
            # book subjects
            self._book_in_subj_model = QSqlTableModel(self, db=db)
            self._book_in_subj_model.setTable("book_in_subject")

    def setup_fields(self):
        """ setting up validators and stuff """
        # validators
        # forcing uppercasing on these fields
        self.edTitle.setValidator(UppercaseValidator())
        self.edAuthor.setValidator(UppercaseValidator())
        self.edSAuthor.setValidator(UppercaseValidator())
        self.edPublisher.setValidator(UppercaseValidator())
        self.edPrice.setValidator(CurrencyValidator(self.edPrice))
        self.edBarcode.setValidator(NumericValidator())
        self.edYear.setMinimum(1900)
        self.edYear.setMaximum(date.today().year)
        self.edYear.setValue(date.today().year)
        # fixing tab order
        self.setTabOrder(self.edPublisher, self.edYear)
        self.setTabOrder(self.edYear, self.edPrice)
        # connecting return key to tab
        lineEditList = self.findChildren(QLineEdit)
        for lineEdit in lineEditList:
            # had some problem with C++ originated objects
            if lineEdit.objectName() not in ["qt_spinbox_lineedit", "edSubject"]:
                lineEdit.returnPressed.connect(lineEdit.focusNextChild)
            # detect changes on line edits
            lineEdit.textChanged.connect(self.check_changes)
        # different behaviour for these
        self.edBarcode.textChanged.connect(self.check_barcode)
        self.edSubject.returnPressed.connect(self.on_btnAddSubject_clicked)

        # completers
        config_completer(self.edSubject, self._subject_model, "name")
        config_completer(self.edAuthor, self._author_model, "name")
        config_completer(self.edSAuthor, self._s_author_model, "name")
        config_completer(self.edPublisher, self._publisher_model, "name")

        # making image clickable
        clickable(self.edImage).connect(self.handle_image)

    def clear_image(self):
        img = QImage(":icons/no_image.png")
        self.set_image(img)
        self._image_set = False
        self.btnCleanImage.setVisible(False)

    def handle_image(self):
        image_path = QFileDialog.getOpenFileName(
            self, "Escolha uma imagem", os.getenv("HOME"), "Imagens (*.png, *.jpg *.bmp)"
        )[0]
        if os.path.exists(image_path):
            self.set_image(QImage(image_path))
            self._image_set = True
            self.btnCleanImage.setVisible(True)

    def set_image(self, img):
        pix = QPixmap.fromImage(img)
        pix = pix.scaled(self.IMG_SIZE[0], self.IMG_SIZE[1], Qt.KeepAspectRatio)
        self.edImage.setPixmap(pix)
        self.edImage.setScaledContents(True)

    def check_changes(self, txt):
        if txt != "":
            self._dirty = True

    def check_barcode(self, txt):
        if len(txt) == self.edBarcode.maxLength():
            self.edBarcode.focusNextChild()

    def _get_id_from_name(self, table, name):
        db = Db_Instance(table + "_fetch_" + name + "_id").get_instance()
        if not db.open():
            return None
        else:
            query = QSqlQuery(db)
            query.prepare("SELECT id FROM %s WHERE name = :name" % table)
            query.bindValue(":name", name)
            query.exec_()
            if query.next():
                return query.record().value("id")
            else:
                return None

    def submit_data(self):
        data = self.extract_input()
        # checking fields that aren't inserted yet
        for val, model in [
            ("author", self._author_model),
            ("s_author", self._s_author_model),
            ("publisher", self._publisher_model),
        ]:
            if isinstance(data[val], unicode):
                # needs to be inserted
                model.insertRow(0)
                model.setData(model.index(0, 1), data[val])
                data[val] = submit_and_get_id(self, model, self.log)
                if not data[val]:
                    # won't proceed if this fails
                    return False
        # filling a book row
        self._model.insertRow(0)
        for key, val in data.items():
            self._model.setData(self._model.index(0, self.column[key]), val)
        book_id = submit_and_get_id(self, self._model, self.log)
        if book_id:
            # for use in selection docks
            self.setBookId(book_id)
            # book sucessfully added, now associating related subjects
            subjects, new_subjects = self.extract_subjects_input()
            for subj in new_subjects:
                self._subject_model.insertRow(0)
                self._subject_model.setData(self._subject_model.index(0, 1), subj)
                id = submit_and_get_id(self, self._subject_model, self.log)
                if not id:
                    # issue saving new subject
                    return False
                subjects.append(int(id))
            # associating book and it's subjects
            error = False
            for subj_id in subjects:
                self._book_in_subj_model.insertRow(0)
                self._book_in_subj_model.setData(self._book_in_subj_model.index(0, 0), book_id)
                self._book_in_subj_model.setData(self._book_in_subj_model.index(0, 1), subj_id)
                ok = self._book_in_subj_model.submitAll()
                if not ok:
                    error = True
                    break
            if error:
                self.log.error(self._book_in_subj_model.lastError.text())
                message = unicode(
                    "Erro\n\n"
                    "Livro cadastrado, porém ocorreu um problema ao"
                    " salvar os temas a que está associado".decode("utf-8")
                )
                QMessageBox.warning(self, "Seareiros - Cadastro de Livro", message)
                return False
            else:
                message = unicode("Sucesso!\n\n" "O livro foi salvo com êxito no banco de dados".decode("utf-8"))
                QMessageBox.information(self, "Seareiros - Cadastro de Livro", message)
                return True
        # failed to insert a row
        return False

    def setBookId(self, id):
        self._book_id = id

    def get_added_record(self):
        db = Db_Instance("added_book_record").get_instance()
        if db.open() and self._book_id:
            query = QSqlQuery(db)
            query.prepare("SELECT * FROM book WHERE id = :id")
            query.bindValue(":id", self._book_id)
            query.exec_()
            if query.next():
                return query.record()
            else:
                return None
        else:
            return None

    def clear(self):
        self._dirty = False
        lineEditList = self.findChildren(QLineEdit)
        for lineEdit in lineEditList:
            lineEdit.clear()
        self.clear_table()
        self.clear_image()
        self.edBarcode.setFocus()

    @QtCore.Slot()
    def on_btnAddSubject_clicked(self):
        txt = self.edSubject.text()
        if txt != "":
            id = self._get_id_from_name("subject", self.edSubject.text())
            if id:
                # known register
                data = [id, txt]
            else:
                # new data
                data = [None, txt]
            not_a_duplicate = self.add_subject(data)
            if not_a_duplicate:
                self.refresh_tableSubjects()
                self.edSubject.clear()
            self.edSubject.setFocus()

    @QtCore.Slot()
    def on_btnCleanSubjects_clicked(self):
        self.clear_table()
        self.edSubject.setFocus()

    def clear_table(self):
        self._subject_list = []
        self.tableSubjects.clear()
        self.refresh_tableSubjects()

    def add_subject(self, data):
        """ adds a subject to the list except for duplicates """
        if data in self._subject_list:
            return False
        else:
            self._subject_list.append(data)
            # sorts by name
            self._subject_list.sort(key=operator.itemgetter(1))
            return True

    def refresh_tableSubjects(self):
        if len(self._subject_list) > 0:
            self.tableSubjects.setColumnCount(len(self._subject_list[0]) + 1)
            col_labels = ["", "Nome", ""]
            self.tableSubjects.setHorizontalHeaderLabels(col_labels)
            self.tableSubjects.setColumnHidden(0, True)
        else:
            self.tableSubjects.setColumnCount(0)
        self.tableSubjects.setRowCount(len(self._subject_list))
        for i, row in enumerate(self._subject_list):
            for j, col in enumerate(row):
                item = QTableWidgetItem(col)
                self.tableSubjects.setItem(i, j, item)
            # icon to remove rows individually
            remove_icon = QIcon(":icons/conn_failed.png")
            remove_btn = QPushButton(remove_icon, "")
            remove_btn.clicked.connect(partial(self.remove_subject, subject=row))
            self.tableSubjects.setCellWidget(i, len(row), remove_btn)
        self.tableSubjects.resizeColumnsToContents()
        self.tableSubjects.horizontalHeader().setResizeMode(1, QHeaderView.Stretch)

    def remove_subject(self, subject):
        # remove a row based on its value
        self._subject_list.remove(subject)
        self.refresh_tableSubjects()

    def extract_input(self):
        data = {}
        data["barcode"] = self.edBarcode.text()
        data["title"] = self.edTitle.text()

        # completer fields
        for c_field, line_edit in [
            ("author", self.edAuthor),
            ("s_author", self.edSAuthor),
            ("publisher", self.edPublisher),
        ]:
            data[c_field] = self._get_cfield_value(c_field, line_edit.text())
        data["year"] = self.edYear.value()
        data["price"] = self._locale.toDouble(self.edPrice.text())[0]
        data["description"] = self.edDescription.toPlainText()
        if self._image_set:
            data["image"] = qpixmap_to_qbytearray(self.edImage.pixmap())

        data["availability"] = self.radioAvailability.checkedId()

        return data

    def _get_cfield_value(self, c_field, text):
        if text == "":
            return None
        id = self._get_id_from_name(c_field, text)
        if id:
            return id
        else:
            return text

    def extract_subjects_input(self):
        # grab id of selected activities
        subjects = []
        new_subjects = []
        for subj in self._subject_list:
            if subj[0]:
                # selected from previously added subjects
                subjects.append(subj[0])
            else:
                # new subject
                new_subjects.append(subj[1])
        return (subjects, new_subjects)