Example #1
0
class DownloadComponent(QGroupBox):
    reportFormatChanged = Signal(ReportWriter)
    saveCompleted = Signal(bool)

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

        self._report_writer = None
        self._report_writer_thread = ReportWriterThread()
        self._report_writer_thread.completed.connect(self._saveCompleted)
        
        self._progress_message = ProgressMessageBox(self.parentWidget(), "Export des données en cours...", "Export des données en cours...", 0, 0)

        self._report_types = {
            "summary": {"name": "Résumé", "description": "Le résumé inclu les statistiques générales sur les images analysées"},
            "full": {"name": "Complet", "description": "Le rapport complet inclu les statistiques détaillées sur les images analysées"},
            "annotations": {"name": "Annotations", "description": "Converti les résultats de l'analyse en annotation utilisable pour l'entrainement"}
        }
        self._report_formats = {
            "summary": {
                "PDF": {"type": "Document PDF", "extension": "*.pdf"},
                "Texte": {"type": "Fichier texte", "extension": "*.txt"}
            },
            "full": {
                "CSV": {"type": "Fichier CSV", "extension": "*.csv"},
                "JSON": {"type": "Fichier JSON", "extension": "*.json"},
                "XML": {"type": "Fichier XML", "extension": "*.xml"},
            },
            "annotations": {
                "Annotations YOLOv4": {"type": "Fichier zip", "extension": "*.zip"}
            }
        }
        self._default_report_type = "summary"

        self.setTitle("Paramètres")
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)

        self._form_layout = QFormLayout()
        self._form_layout.setHorizontalSpacing(20)
        self._form_layout.setVerticalSpacing(14)

        self._report_type_cbox = QComboBox()
        for report_type, v in self._report_types.items():
            self._report_type_cbox.addItem(v["name"], report_type)

        self._form_layout.addRow("Type de rapport :", self._report_type_cbox)

        self._info_text = QLabel(self._report_types[self._default_report_type]["description"])
        self._info_text.setObjectName("info")
        self._form_layout.addRow(self._info_text)

        self._report_format_cbox = QComboBox()
        for format, data in self._report_formats[self._default_report_type].items():
            self._report_format_cbox.addItem(format, data)

        self._form_layout.addRow("Format du rapport :", self._report_format_cbox)

        self._detection_shape_cbox = QComboBox()
        self._detection_shape_cbox.addItem("Rectangle", "rectangle")
        self._detection_shape_cbox.addItem("Cercle", "circle")
        self._detection_shape_cbox.hide()

        self._separator_cbox = QComboBox()
        self._separator_cbox.addItem("Point virgule", ";")
        self._separator_cbox.addItem("Virgule", ",")
        self._separator_cbox.hide()

        self._nb_keeped_spinbox = QSpinBox(self)
        self._nb_keeped_spinbox.setRange(1, 1)
        self._nb_keeped_spinbox.hide()

        download_button = StylizedButton("Télécharger", "blue")
        download_button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)

        button_layout = QHBoxLayout()
        button_layout.setAlignment(Qt.AlignRight)
        button_layout.addWidget(download_button)

        main_layout = QVBoxLayout(self)
        main_layout.addLayout(self._form_layout)
        main_layout.addStretch(1)
        main_layout.addLayout(button_layout)

        # Signals
        download_button.clicked.connect(self._exportReport)
        self._report_type_cbox.currentIndexChanged.connect(self._reportTypeChanged)
        self._report_format_cbox.currentTextChanged.connect(self._reportFormatChanged)
        self._detection_shape_cbox.currentIndexChanged.connect(self._reportParamsChanged)
        self._separator_cbox.currentIndexChanged.connect(self._reportParamsChanged)

    def reset(self, analysis: Analysis):
        self._analysis = analysis

        self._report_type_cbox.setCurrentIndex(0)
        self._report_format_cbox.setCurrentIndex(0)
        self._nb_keeped_spinbox.setMaximum(analysis.imagesWithDetections())
        self._nb_keeped_spinbox.setValue(min(100, analysis.imagesWithDetections()))
        self._loadWriter()

    def _loadWriter(self):
        format_text = self._report_format_cbox.currentText()
        if format_text == "PDF":
            self._report_writer = PDFReportWriter(self._analysis)
        
        elif format_text == "Texte":
            self._report_writer = TextReportWriter(self._analysis)

        elif format_text == "CSV":
            self._report_writer = CSVReportWriter(self._analysis, self._separator_cbox.currentData(), self._detection_shape_cbox.currentData())

        elif format_text == "JSON":
            self._report_writer = JSONReportWriter(self._analysis, shape=self._detection_shape_cbox.currentData())
        
        elif format_text == "XML":
            self._report_writer = XMLReportWriter(self._analysis, shape=self._detection_shape_cbox.currentData())

        elif format_text == "Annotations YOLOv4":
            self._report_writer = Yolov4AnnotationsWriter(self._analysis)

        self.reportFormatChanged.emit(self._report_writer)

    @Slot(int)
    def _reportTypeChanged(self, index: int):
        report_type = self._report_type_cbox.currentData()
        self._info_text.setText(self._report_types[report_type]["description"])

        self._report_format_cbox.clear()
        for format, data in self._report_formats[report_type].items():
            self._report_format_cbox.addItem(format, data)

        if report_type == "summary":
            self._showDetectionShape(False)
            self._showSeparator(False)
            self._showKeepedNumber(False)
        elif report_type == "full":
            self._showDetectionShape(True)
            self._showKeepedNumber(False)
        elif report_type == "annotations":
            self._showDetectionShape(False)
            self._showSeparator(False)
            self._showKeepedNumber(True)
    
    @Slot(str)
    def _reportFormatChanged(self, format: str):
        if format == "CSV":
            self._showSeparator(True)
        else: # JSON & XSML
            self._showSeparator(False)

        self._loadWriter()

    def _showDetectionShape(self, show: bool):
        self._showRow("Détection :", self._detection_shape_cbox, show)

    def _showSeparator(self, show: bool):
        self._showRow("Séparateur :", self._separator_cbox, show)

    def _showKeepedNumber(self, show: bool):
        self._showRow("Images à exporter :", self._nb_keeped_spinbox, show)

    def _showRow(self, label: str, widget: QWidget, show: bool):
        if show and not widget.isVisible():
            widget.show()
            self._form_layout.addRow(label, widget)
        elif not show and widget.isVisible():
            label = self._form_layout.labelForField(widget)
            widget.hide()
            label.hide()
            self._form_layout.removeWidget(widget)
            self._form_layout.removeWidget(label)
            del label

    @Slot(int)
    def _reportParamsChanged(self, index: int):
        self._loadWriter()

    @Slot()
    def _exportReport(self):
        report_type = self._report_type_cbox.currentData()
        if report_type == "annotations":
            self._report_writer.setKeep(self._nb_keeped_spinbox.value())

        error = self._report_writer.checkErrors()
        if error:
            QMessageBox.warning(self.parentWidget(), "Impossible de générer le rapport", error)
            return

        filename = self._analysis.parameters().name()
        report_format = self._report_format_cbox.currentData()

        path = QStandardPaths.writableLocation(QStandardPaths.DocumentsLocation) + "/" + filename
        filter = "Fichier %s (%s)" % (report_format["type"], report_format["extension"])

        result = QFileDialog.getSaveFileName(self, "Enregistrer le fichier", path, filter)
        
        filepath = result[0]
        if filepath == '':
            return

        self._report_writer_thread.start(self._report_writer, filepath)
        self._progress_message.exec_()

    @Slot()
    def _saveCompleted(self, success: bool):
        if self._progress_message:
            self._progress_message.close()
        
        self.saveCompleted.emit(success)
Example #2
0
class _RemoveNanEditor(AbsOperationEditor):
    _baseText = {
        0: 'Remove with more than: <b>{}</b> nan',
        1: 'Remove with more than: <b>{}%</b> nan'
    }

    def __init__(self, mode: str, parent: QWidget = None):
        """ Builds the editor

        :param mode: one of 'col' or 'row'
        :param parent: a parent widget

        """
        self.__mode: str = mode
        super().__init__(parent)

    def editorBody(self) -> QWidget:
        self.__group = QButtonGroup()
        self.__group.setExclusive(True)
        lab = QLabel('Choose how to remove:')
        self.__group.addButton(QRadioButton('By number'), id=0)
        self.__group.addButton(QRadioButton('By percentage'), id=1)
        self.__currId = None

        self.__summaryLabel = QLabel()
        self.__slider = QSlider(Qt.Horizontal, self)
        self.__slider.setMinimum(0)
        self.__slider.setTracking(True)
        self.__slider.setSingleStep(1)

        self.__numBox = QSpinBox()
        self.__numBox.setMinimum(0)
        self.__numBox.setMaximum(10000000)

        radioLayout = QHBoxLayout()
        radioLayout.addWidget(self.__group.button(0))
        radioLayout.addWidget(self.__group.button(1))
        self.__bodyLayout = QVBoxLayout()
        self.__bodyLayout.addWidget(lab)
        self.__bodyLayout.addLayout(radioLayout)
        self.__bodyLayout.addSpacing(20)
        self.__bodyLayout.addWidget(QLabel('Move the slider to set removal parameter:'))
        self.__bodyLayout.addSpacing(10)
        self.__bodyLayout.addWidget(self.__slider if self.__mode == 'row' else self.__numBox)
        self.__bodyLayout.addWidget(self.__summaryLabel)

        self.__group.buttonClicked[int].connect(self._toggleMode)
        # Both are connected, only one is shown
        self.__slider.valueChanged.connect(self._onValueChanged)
        self.__numBox.valueChanged[int].connect(self._onValueChanged)
        # Set a default button and label text
        self.__group.button(0).click()
        self.__summaryLabel.setText(self._baseText[0].format(self.__slider.minimum()))

        body = QWidget()
        body.setLayout(self.__bodyLayout)
        return body

    @Slot(int)
    def _toggleMode(self, bid: int) -> None:
        # NOTE: could be refactored
        if bid == self.__currId:
            return
        self.__currId = bid
        if bid == 0:
            if not (self.inputShapes and self.inputShapes[0]) and self.__mode == 'row':
                self.__slider.setDisabled(True)
                self._onValueChanged(self.__slider.value())
            elif not self.__slider.isEnabled():
                self.__slider.setEnabled(True)
            else:
                if self.__mode == 'row':
                    self.__slider.setMaximum(self.inputShapes[0].nColumns)
                    self._onValueChanged(self.__slider.value())
                else:
                    self.__bodyLayout.replaceWidget(self.__slider, self.__numBox)
                    self.__slider.hide()
                    self.__numBox.show()
                    self._onValueChanged(self.__numBox.value())
        else:
            if self.__mode == 'row':
                if not self.__slider.isEnabled():
                    self.__slider.setEnabled(True)
            else:
                self.__bodyLayout.replaceWidget(self.__numBox, self.__slider)
                self.__numBox.hide()
                self.__slider.show()
            self._onValueChanged(self.__slider.value())
            self.__slider.setMaximum(100)

    @Slot(int)
    def _onValueChanged(self, value: int):
        self.__summaryLabel.setText(self._baseText[self.__currId].format(value))

    def getOptions(self) -> Iterable:
        if self.__group.checkedId() == 0:
            # By number
            return None, self.__slider.value() if self.__mode == 'row' else self.__numBox.value()
        else:
            # By perc
            return self.__slider.value() / 100, None

    def setOptions(self, percentage: float, number: int) -> None:
        if percentage is not None:
            self.__group.button(1).click()
            self.__slider.setValue(percentage * 100)
        elif number is not None:
            self.__group.button(0).click()
            self.__slider.setValue(number) if self.__mode == 'row' else self.__numBox.setValue(number)
        else:
            # Both None
            self.__slider.setValue(0)
            self.__numBox.setValue(0)

    def refresh(self) -> None:
        if self.__mode == 'row' and self.__group.checkedId() == 0:
            self.__slider.setMaximum(self.inputShapes[0].nColumns)
            self.__slider.setEnabled(True)
Example #3
0
class Bucket(QWidget):
    IMG_EMPTY = QIcon("img/Empty_bucket.svg")
    IMG_FULL = QIcon("img/Full_bucket.svg")
    IMG_HALF = QIcon("img/Half_full_bucket.svg")
    selected = Signal(object)
    removed = Signal(object)

    def __init__(self, capacity, init, goal):
        super().__init__()
        self.capacity = capacity
        self.init = init
        self.current = init
        self.goal = goal
        self.editable = False
        self.btn = QPushButton()
        self.btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.btn.clicked.connect(self.select)
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.btn)

        self.btn_remove = QPushButton("X")
        self.btn_remove.clicked.connect(self.remove)
        self.layout.addWidget(self.btn_remove)

        self.edit_capacity = QSpinBox()
        self.edit_capacity.setPrefix("Capacity ")
        self.edit_capacity.setSuffix(" L")
        self.edit_capacity.valueChanged.connect(self.change_capacity)
        self.layout.addWidget(self.edit_capacity)
        self.edit_capacity.hide()
        self.edit_init = QSpinBox()
        self.edit_init.setPrefix("Init ")
        self.edit_init.setSuffix(" L")
        self.edit_init.valueChanged.connect(self.change_init)
        self.layout.addWidget(self.edit_init)
        self.edit_init.hide()
        self.edit_goal = QSpinBox()
        self.edit_goal.setPrefix("Goal ")
        self.edit_goal.setSuffix(" L")
        self.edit_goal.valueChanged.connect(self.change_goal)
        self.layout.addWidget(self.edit_goal)
        self.edit_goal.hide()
        self.update()
        self.setLayout(self.layout)

    def switch_editable(self):
        self.editable = not self.editable
        if self.editable:
            self.edit_capacity.show()
            self.edit_init.show()
            self.edit_goal.show()
            self.btn.setEnabled(False)
        else:
            self.edit_capacity.hide()
            self.edit_init.hide()
            self.edit_goal.hide()
            self.btn.setEnabled(True)

    def change_capacity(self, capacity):
        self.capacity = capacity
        self.update()

    def change_init(self, init):
        self.init = init
        self.current = init
        self.update()

    def change_goal(self, goal):
        self.goal = goal
        self.update()

    def reset(self):
        self.current = self.init
        self.update()

    def remove(self):
        self.removed.emit(self)

    def select(self):
        self.selected.emit(self)

    def update(self):
        self.update_spin()
        self.update_image()
        self.update_text()

    def update_spin(self):
        self.edit_capacity.setValue(self.capacity)
        self.edit_capacity.setMinimum(self.init)
        self.edit_init.setValue(self.init)
        self.edit_init.setMaximum(self.capacity)
        self.edit_goal.setValue(self.goal)
        self.edit_goal.setMaximum(self.capacity)

    def update_text(self):
        self.btn.setText(f"{self.current} ({self.goal}) / {self.capacity} L")

    def update_image(self):
        if self.current == 0:
            self.btn.setIcon(self.IMG_EMPTY)
        elif self.current == self.capacity:
            self.btn.setIcon(self.IMG_FULL)
        else:
            self.btn.setIcon(self.IMG_HALF)

    def emptying(self, bucket):
        value = min([self.current, bucket.capacity - bucket.current])
        self.current -= value
        self.update()
        bucket.current += value
        bucket.update()

    def full(self):
        return self.capacity == self.current

    def good(self):
        return self.goal == self.current

    def changed_color(self, actived=False, color=Qt.blue):
        pal = QPushButton().palette()  # self.btn
        if actived:
            pal.setColor(QPalette.Button, QColor(color))
        elif self.good():
            pal.setColor(QPalette.Button, QColor(Qt.green))
        self.btn.setPalette(pal)
Example #4
0
class application(QTabWidget):
    bot = 0

    def __init__(self, parent=None):
        super(application, self).__init__(parent)
        self.bot = None
        self.db = sqlite3.connect('database')
        # tabs
        self.tab1 = QWidget()
        self.tab2 = QWidget()
        self.tab3 = QWidget()
        self.tab4 = QWidget()
        self.tab5 = QWidget()
        self.resize(640, 400)

        self.addTab(self.tab1, "Tab 1")
        self.addTab(self.tab2, "Tab 2")
        self.addTab(self.tab3, "Tab 3")
        self.addTab(self.tab4, "Tab 4")
        self.addTab(self.tab5, "Tab 5")

        # tab set keys
        self.h_box_key = QHBoxLayout()
        self.change_key_b = QPushButton("Edit keys")
        self.edit_1 = QLineEdit()
        self.edit_2 = QLineEdit()
        self.edit_3 = QLineEdit()
        self.edit_4 = QLineEdit()
        self.result = QLabel()
        self.set_button = QPushButton("Set keys")
        self.handle_info = QLabel()
        self.follower_info = QLabel()
        self.ready_lab = QLabel()

        # tab follow
        self.box_label = QLabel("Link to tweet")
        self.combo_label = QLabel("Mode")
        self.spin_label = QLabel("Limit before sleep")
        self.prog_bar = QProgressBar()
        self.combo_box = QComboBox()
        self.h_box = QHBoxLayout()
        self.spin_box = QSpinBox()
        self.link_box = QLineEdit()
        self.link_result = QLabel()
        self.follow_button = QPushButton("Follow Retweeters")
        self.cancel_button = QPushButton("Cancel")
        self.logger = QPlainTextEdit()
        self.h_box2 = QHBoxLayout()
        self.max_box = QSpinBox()
        self.max_label = QLabel("Max follows before stop")

        # tab unfollow
        self.unfollow_button = QPushButton("Unfollow Auto followers")
        self.unf_logger = QPlainTextEdit()
        self.unfollow_res = QLabel()
        self.prog_bar_unf = QProgressBar()
        self.unfollow_cancel = QPushButton("Cancel")
        self.unf_confirm = QMessageBox()

        # tab help
        self.help_box = QPlainTextEdit()
        self.help_label = QLabel(
            "<a href='http://Optumsense.com/'>http://Optumsense.com/</a>")

        #tab schedule
        self.tweet_box = QPlainTextEdit()
        self.date_time = QDateTimeEdit(QDateTime.currentDateTime())
        self.schedule_but = QPushButton("Schedule Tweet")
        self.schedule_info = QLabel()
        self.schedule_table = QTableView()

        # threads
        self.follow_thread = None
        self.unfollow_thread = None
        self.schedule_thread = None

        # tabs
        self.tab1UI()
        self.tab2UI()
        self.tab3UI()
        self.tab4UI()
        self.tab5UI()

        self.setWindowTitle("Optumize")
        self.setWindowIcon(QtGui.QIcon('assets/oo.png'))

        # db
        cursor = self.db.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS keys(one TEXT, two TEXT, three TEXT, four TEXT)'''
                       )
        self.db.commit()

    def tab1UI(self):
        layout = QFormLayout()
        layout.addRow(self.h_box)
        self.h_box.addWidget(self.combo_label)
        self.combo_label.setAlignment(Qt.AlignRight)
        self.h_box.addWidget(self.combo_box)
        self.combo_box.addItem("Follow Retweeters")
        self.combo_box.addItem("Follow Followers")
        self.combo_box.currentIndexChanged.connect(self.selection_change)

        self.h_box.addWidget(self.spin_label)
        self.spin_label.setAlignment(Qt.AlignRight)
        self.h_box.addWidget(self.spin_box)
        self.spin_box.setMinimum(1)
        self.spin_box.setValue(30)

        layout.addRow(self.box_label, self.link_box)
        self.link_result.setAlignment(Qt.AlignCenter)
        layout.addRow(self.link_result)
        layout.addRow(self.follow_button)
        self.follow_button.clicked.connect(self.follow_ret)

        layout.addRow(self.cancel_button)
        self.cancel_button.clicked.connect(self.cancel_onclick)
        layout.addRow(self.h_box2)
        self.h_box2.addWidget(self.max_label)
        self.h_box2.addWidget(self.max_box)
        self.max_box.setFixedWidth(100)
        self.max_label.setAlignment(Qt.AlignRight)
        self.max_box.setMaximum(1000000)
        self.max_box.setValue(100)
        self.max_label.hide()
        self.max_box.hide()
        layout.addRow(self.logger)

        self.logger.setReadOnly(True)
        layout.addRow(self.prog_bar)
        self.prog_bar.setAlignment(Qt.AlignCenter)
        self.setTabText(0, "Follow")
        self.setTabIcon(0, QtGui.QIcon('assets/check_mark.png'))
        self.tab1.setLayout(layout)

    def selection_change(self, i):
        if i == 0:
            self.box_label.setText("Link to tweet")
            self.follow_button.setText("Follow Retweeters")
            self.link_result.setText("")
            self.max_label.hide()
            self.max_box.hide()
            self.follow_button.clicked.connect(self.follow_ret)
        else:
            self.box_label.setText("Handle of user")
            self.follow_button.setText("Follow Followers")
            self.link_result.setText("")
            self.max_label.show()
            self.max_box.show()
            self.max_label.setText("Max follows before stop")
            self.follow_button.clicked.connect(self.follow_fol)

    def cancel_onclick(self):
        if self.follow_thread is None:
            pass
        elif self.follow_thread.isRunning():
            self.prog_bar.setValue(0)
            self.logger.appendPlainText("Cancelled script")
            self.follow_thread.terminate()
            self.follow_thread = None

    def follow_ret(self):
        self.prog_bar.setValue(0)
        self.follow_button.setEnabled(False)
        self.link_result.setText("")
        self.logger.clear()
        limit = self.spin_box.value()
        if self.bot is None:
            self.link_result.setText(
                "<font color='red'>Configure access keys in set keys tab</font>"
            )
            return
        if self.follow_thread is not None:
            return
        link = self.link_box.text()
        id_tweet = link.split("/")[-1]
        try:
            tweet = self.bot.api.get_status(id_tweet)

            self.logger.appendPlainText(
                f"following retweeters from link: {link}...")
            self.follow_thread = FollowThread(self.bot, id_tweet, limit, 1, 0)
            self.follow_thread.start()
            self.connect(self.follow_thread, SIGNAL("finished()"), self.done)
            self.connect(self.follow_thread, SIGNAL("setup_prog(QString)"),
                         self.setup_prog)
            self.connect(self.follow_thread, SIGNAL("post_follow(QString)"),
                         self.post_follow)
        except tweepy.error.TweepError:
            self.follow_button.setEnabled(True)
            self.link_result.setText(
                "<font color='red'>Could not find tweet</font>")

    def setup_prog(self, msg):
        self.prog_bar.setMaximum(int(msg))

    def follow_fol(self):
        self.prog_bar.setValue(0)
        self.follow_button.setEnabled(False)
        self.link_result.setText("")
        self.logger.clear()
        limit = self.spin_box.value()
        if self.bot is None:
            self.link_result.setText(
                "<font color='red'>Configure access keys in set keys tab</font>"
            )
            return
        if self.follow_thread is not None:
            return
        handle = self.link_box.text()
        if handle == '':
            self.link_result.setText(
                "<font color='red'>Enter a handle above</font>")
            return
        elif handle[0] == '@':
            id_user = handle[1:]
        else:
            id_user = handle
        try:
            man = self.bot.api.get_user(id_user)
            self.logger.appendPlainText(
                f"following followers of {man.screen_name}...")
            self.logger.appendPlainText(f"Collecting")
            self.follow_thread = FollowThread(self.bot, id_user, limit, 2,
                                              self.max_box.value())
            self.follow_thread.start()
            self.connect(self.follow_thread, SIGNAL("finished()"), self.done)
            self.connect(self.follow_thread, SIGNAL("setup_prog(QString)"),
                         self.setup_prog)
            self.connect(self.follow_thread, SIGNAL("post_follow(QString)"),
                         self.post_follow)
        except tweepy.error.TweepError:
            self.follow_button.setEnabled(True)
            self.link_result.setText(
                "<font color='red'>Could not find user</font>")

    def post_follow(self, message):
        if message == "bad":
            self.logger.appendPlainText(
                "Rate limit exceeded... sleeping for cooldown")
        else:
            self.logger.appendPlainText(message)
            self.prog_bar.setValue(self.prog_bar.value() + 1)

    def done(self):
        self.follow_thread = None
        self.follow_button.setEnabled(True)

    def tab2UI(self):
        layout = QFormLayout()
        layout.addRow(self.unfollow_button)
        layout.addRow(self.unfollow_cancel)
        layout.addRow(self.unfollow_res)
        self.unfollow_res.setAlignment(Qt.AlignCenter)
        self.unfollow_button.clicked.connect(self.unfollow_fol)
        self.unfollow_cancel.clicked.connect(self.unfollow_can)
        layout.addWidget(self.unf_logger)
        self.unf_logger.setReadOnly(True)
        layout.addRow(self.prog_bar_unf)
        self.prog_bar_unf.setAlignment(Qt.AlignCenter)
        self.setTabText(1, "Unfollow")
        self.setTabIcon(1, QtGui.QIcon('assets/cross.png'))
        self.tab2.setLayout(layout)

    def unfollow_fol(self):
        self.unfollow_button.setEnabled(False)
        self.unfollow_thread = UnfollowThread(self.bot)
        self.unfollow_thread.start()
        self.connect(self.unfollow_thread, SIGNAL("post_unfol(QString)"),
                     self.post_unfol)
        self.connect(self.unfollow_thread, SIGNAL("finished()"), self.done_unf)

    def done_unf(self):
        self.unfollow_thread = None
        self.unf_logger.appendPlainText("Done")
        self.unfollow_button.setEnabled(True)

    def post_unfol(self, msg):
        if msg == "bad":
            self.unf_logger.appendPlainText(
                "rate limit exceeded, resting for 15 minutes")
        else:
            self.unf_logger.appendPlainText(f"Unfollowing {msg}")

    def unfollow_can(self):
        if self.unfollow_thread is None:
            pass
        elif self.unfollow_thread.isRunning():
            self.unf_logger.appendPlainText("Cancelled script")
            self.unfollow_thread.terminate()
            self.unfollow_thread = None

    def tab3UI(self):
        layout = QFormLayout()
        layout.addWidget(self.tweet_box)
        self.tweet_box.setMaximumHeight(150)
        self.tweet_box.setPlaceholderText("Tweet contents")
        layout.addRow(self.date_time)
        self.date_time.setCalendarPopup(True)
        layout.addRow(self.schedule_info)
        layout.addRow(self.schedule_but)
        self.schedule_but.clicked.connect(self.schedule_tweet)
        layout.addWidget(self.schedule_table)
        self.setTabText(2, "Schedule Tweet")
        self.setTabIcon(2, QtGui.QIcon('assets/calendar.png'))
        self.tab3.setLayout(layout)

    def schedule_tweet(self):
        tweet_contents = self.tweet_box.toPlainText()
        if (len(tweet_contents) == 0):
            print("length of tweet is 0")
            self.schedule_info.setText("length of tweet is 0")
            return
        elif (len(tweet_contents) >= 280):
            self.schedule_info.setText("Tweet char limit exceeded")
            return
        if self.bot is None:
            self.schedule_info.setText(
                "<font color='red'>Configure access keys in set keys tab</font>"
            )
            return
        datetime = self.date_time.text()
        try:
            print("done")
            self.schedule_thread.start()
            self.schedule_thread.add_tweet(tweet_contents, datetime)
        except:
            self.follow_button.setEnabled(True)
            self.link_result.setText(
                "<font color='red'>Could not find user</font>")

    def done_schedule(self):
        print("done scheduler")

    def tab4UI(self):
        layout = QFormLayout()
        layout.addRow("API key", self.edit_1)
        layout.addRow("API key secret", self.edit_2)
        layout.addRow("Auth token", self.edit_3)
        layout.addRow("Auth token secret", self.edit_4)
        self.set_button.clicked.connect(self.set_keys)
        l = self.read_file()
        if l is not None:
            if len(l) == 4:
                self.edit_1.setText(l[0])
                self.edit_2.setText(l[1])
                self.edit_3.setText(l[2])
                self.edit_4.setText(l[3])
                self.set_keys()
        layout.addRow(self.result)
        self.result.setAlignment(Qt.AlignCenter)
        layout.addRow(self.h_box_key)
        self.h_box_key.addWidget(self.change_key_b)
        self.h_box_key.addWidget(self.set_button)
        self.change_key_b.clicked.connect(self.change_keys)
        layout.addRow(self.handle_info)
        self.handle_info.setAlignment(Qt.AlignCenter)
        layout.addRow(self.follower_info)
        self.follower_info.setAlignment(Qt.AlignCenter)
        layout.addRow(self.ready_lab)
        self.ready_lab.setAlignment(Qt.AlignCenter)
        self.setTabText(3, "Settings")
        self.setTabIcon(3, QtGui.QIcon('assets/settings.png'))
        self.tab4.setLayout(layout)

    def change_keys(self):
        self.set_button.setEnabled(True)

    def set_keys(self):
        self.set_button.setEnabled(False)
        self.result.setText("")
        one = self.edit_1.text()
        two = self.edit_2.text()
        three = self.edit_3.text()
        four = self.edit_4.text()
        try:
            self.bot = apiconnector.ApiConnector(one, two, three, four)
            me = self.bot.add_keys(one, two, three, four)
            self.handle_info.setText("Handle: @" + me.screen_name)
            self.follower_info.setText("Followers: " + str(me.followers_count))
            self.ready_lab.setText("<font color='green'>Ready!</font>")
            cursor = self.db.cursor()
            cursor.execute('DELETE FROM keys;', )
            cursor.execute(
                '''INSERT INTO keys(one, two, three, four)
                  VALUES(?,?,?,?)''', (one, two, three, four))
            self.db.commit()
            self.schedule_thread = ScheduleThread(self.bot)
        except:
            print("Could not authenticate you")
            self.result.setText(
                "<font color='red'>Could not authenticate you</font>")

    def read_file(self):
        result = []
        try:
            cursor = self.db.cursor()
            cursor.execute('''SELECT one, two, three, four FROM keys''')
            all_rows = cursor.fetchall()
            for row in all_rows:
                result.append(row[0])
                result.append(row[1])
                result.append(row[2])
                result.append(row[3])
            return result

        except:
            return None

    def tab5UI(self):
        layout = QFormLayout()
        layout.addRow("Website", self.help_label)
        self.help_label.setOpenExternalLinks(True)
        self.setTabText(4, "Help")
        self.setTabIcon(4, QtGui.QIcon('assets/help.png'))
        self.tab5.setLayout(layout)