Esempio n. 1
0
 def testRefcount(self):
     textedit = QTextEdit()
     textedit.setReadOnly(True)
     doc = textedit.document()
     cursor = QTextCursor(doc)
     cursor.insertText("PySide Rocks")
     ud = TestUserData({"Life": 42})
     self.assertEqual(sys.getrefcount(ud), 2)
     cursor.block().setUserData(ud)
     self.assertEqual(sys.getrefcount(ud), 3)
     ud2 = cursor.block().userData()
     self.assertEqual(sys.getrefcount(ud), 4)
     self.udata = weakref.ref(ud, None)
     del ud, ud2
     self.assertEqual(sys.getrefcount(self.udata()), 2)
Esempio n. 2
0
    def build_invoice(self, data):
        document = QTextDocument()
        self.setDocument(document)
        document.setPageSize(QSizeF(self.doc_width, self.doc_height))
        document.setDefaultFont(font)
        cursor = QTextCursor(document)
        cursor.insertText(f"Customer Name: {data['c_name']}\n")
        cursor.insertText(f"Customer Address: {data['c_addr']}\n")
        cursor.insertText(f"Date: {data['i_date']}\n")
        cursor.insertText(f"Total Due: {data['total_due']}\n")
# +        
        return document                                                         # +++
Esempio n. 3
0
    def on_log_received(self, data):
        time_info = datetime.fromtimestamp((data['time'] / 1000)).isoformat()
        log_message = '%s: %s : %s' % (time_info, data['level'],
                                       data['message'])
        message_document = self.document()
        cursor_to_add = QTextCursor(message_document)
        cursor_to_add.movePosition(cursor_to_add.End)
        cursor_to_add.insertText(log_message + '\n')

        if data['level'] in COLORS:
            fmt = QTextCharFormat()
            fmt.setForeground(COLORS[data['level']])
            cursor_to_add.movePosition(cursor_to_add.PreviousBlock)
            log_lvl_data = LogLevelData(log_levels[data['level'].upper()])
            cursor_to_add.block().setUserData(log_lvl_data)
            cursor_to_add_fmt = message_document.find(data['level'],
                                                      cursor_to_add.position())
            cursor_to_add_fmt.mergeCharFormat(fmt)
            if log_levels[data['level']] > self.log_lvl:
                cursor_to_add.block().setVisible(False)
        self.ensureCursorVisible()
Esempio n. 4
0
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.signals = Signals()
        self.initUI()
        self.setSignals()
        self.decensor = Decensor(self)
        self.load_model()

    def initUI(self):

        grid_layout = QGridLayout()
        grid_layout.setSpacing(10)
        self.setLayout(grid_layout)

        #Tutorial
        self.tutorialLabel = QLabel()
        self.tutorialLabel.setText(
            "Welcome to DeepCreamPy!\n\nIf you're new to DCP, please read the README.\nThis program does nothing without the proper setup of your images.\n\nReport any bugs you encounter to me on Github or Twitter @deeppomf."
        )
        self.tutorialLabel.setAlignment(Qt.AlignCenter)
        self.tutorialLabel.setFont(QFont('Sans Serif', 13))

        #Censor type group
        self.censorTypeGroupBox = QGroupBox('Censor Type')

        barButton = QRadioButton('Bar censor')
        mosaicButton = QRadioButton('Mosaic censor')
        barButton.setChecked(True)

        censorLayout = QVBoxLayout()
        censorLayout.addWidget(barButton)
        censorLayout.addWidget(mosaicButton)
        # censorLayout.addStretch(1)
        self.censorTypeGroupBox.setLayout(censorLayout)

        #Variation count group
        self.variationsGroupBox = QGroupBox('Number of Decensor Variations')

        var1Button = QRadioButton('1')
        var2Button = QRadioButton('2')
        var3Button = QRadioButton('4')
        var1Button.setChecked(True)

        varLayout = QVBoxLayout()
        varLayout.addWidget(var1Button)
        varLayout.addWidget(var2Button)
        varLayout.addWidget(var3Button)
        # varLayout.addStretch(1)
        self.variationsGroupBox.setLayout(varLayout)

        #Decensor button
        self.decensorButton = QPushButton('Decensor Your Images')
        self.decensorButton.clicked.connect(self.decensorClicked)
        self.decensorButton.setSizePolicy(QSizePolicy.Preferred,
                                          QSizePolicy.Preferred)

        #Progress message
        # self.progressGroupBox = QGroupBox('Progress')

        self.progressMessage = QTextEdit()
        self.progressCursor = QTextCursor(self.progressMessage.document())
        self.progressMessage.setTextCursor(self.progressCursor)
        self.progressMessage.setReadOnly(True)
        self.progressCursor.insertText(
            "After you prepared your images, click on the decensor button once to begin decensoring.\nPlease be patient.\nDecensoring will take time.\n"
        )

        # Progress Bar
        self.statusBar = QStatusBar(self)
        self.progressBar = QProgressBar()
        self.progressBar.setMinimum(0)
        self.progressBar.setMaximum(100)
        self.progressBar.setValue(0)
        self.statusLabel = QLabel("Showing Progress")

        self.statusBar.addWidget(self.statusLabel, 1)
        self.statusBar.addWidget(self.progressBar, 2)

        #put all groups into grid
        # addWidget(row, column, rowSpan, columnSpan)
        grid_layout.addWidget(self.tutorialLabel, 0, 0, 1, 2)
        grid_layout.addWidget(self.censorTypeGroupBox, 1, 0, 1, 1)
        grid_layout.addWidget(self.variationsGroupBox, 1, 1, 1, 1)
        grid_layout.addWidget(self.decensorButton, 2, 0, 1, 2)
        grid_layout.addWidget(self.progressMessage, 3, 0, 1, 2)
        grid_layout.addWidget(self.statusBar, 4, 0, 1, 2)

        #window size settings
        self.resize(900, 600)
        self.center()
        self.setWindowTitle('DeepCreamPy v2.2.0-beta')
        self.show()

    def load_model(self):
        # load model to make able to decensor several times
        self.decensorButton.setEnabled(False)
        self.decensorButton.setText(
            "Loading Machine Learning Model (Please Wait...)")
        self.decensor.start()
        self.decensor.signals = self.signals
        self.progressCursor.insertText(
            "Loading Decensor app consumes 6 GB memory at maximum")

    def setSignals(self):
        self.signals.update_decensorButton_Text.connect(
            self.decensorButton.setText)
        self.signals.update_decensorButton_Enabled.connect(
            self.decensorButton.setEnabled)
        self.signals.update_statusLabel_Text.connect(self.statusLabel.setText)
        self.signals.update_ProgressBar_SET_VALUE.connect(
            self.progressBar.setValue)
        self.signals.update_ProgressBar_MAX_VALUE.connect(
            self.progressBar.setMaximum)
        self.signals.update_ProgressBar_MIN_VALUE.connect(
            self.progressBar.setMinimum)
        # self.signals.insertText_progressCursor.connect(self.progressCursor.insertText)
        self.signals.insertText_progressCursor.connect(
            self.progressMessage.append)
        self.signals.clear_progressMessage.connect(self.progressMessage.clear)
        self.signals.appendText_progressMessage.connect(
            self.progressMessage.append)

    def decensorClicked(self):
        self.decensorButton.setEnabled(False)
        self.progressMessage.clear()
        self.progressCursor.insertText("Decensoring has begun!\n")

        # for now, decensor is initiated when this app is started
        # self.decensor = Decensor(text_edit = self.progressMessage, text_cursor = self.progressCursor, ui_mode = True)

        #https://stackoverflow.com/questions/42349470/pyqt-find-checked-radiobutton-in-a-group
        #set decensor to right settings
        #censor type
        censorTypeElements = self.censorTypeGroupBox.children()
        censorButtons = [
            elem for elem in censorTypeElements
            if isinstance(elem, QRadioButton)
        ]
        for cb in censorButtons:
            if cb.isChecked():
                censorType = cb.text()
        if censorType == 'Bar censor':
            self.decensor.is_mosaic = False
        else:
            self.decensor.is_mosaic = True

        #variations count
        variationsElements = self.variationsGroupBox.children()
        variationsButtons = [
            elem for elem in variationsElements
            if isinstance(elem, QRadioButton)
        ]
        for vb in variationsButtons:
            if vb.isChecked():
                variations = int(vb.text())
        self.decensor.variations = variations

        self.decensorButton.setEnabled(False)
        self.decensor.start()
        # decensor.decensor_all_images_in_folder()

    # #centers the main window
    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())
Esempio n. 5
0
    def testCase(self):
        editor = QTextEdit()
        cursor = QTextCursor(editor.textCursor())
        cursor.movePosition(QTextCursor.Start)

        mainFrame = cursor.currentFrame()

        plainCharFormat = QTextCharFormat()
        boldCharFormat = QTextCharFormat()
        boldCharFormat.setFontWeight(QFont.Bold)
        cursor.insertText(
            """
                          Text documents are represented by the 
                          QTextDocument class, rather than by QString objects. 
                          Each QTextDocument object contains information about 
                          the document's internal representation, its structure, 
                          and keeps track of modifications to provide undo/redo 
                          facilities. This approach allows features such as the 
                          layout management to be delegated to specialized 
                          classes, but also provides a focus for the framework.""",
            plainCharFormat)

        frameFormat = QTextFrameFormat()
        frameFormat.setMargin(32)
        frameFormat.setPadding(8)
        frameFormat.setBorder(4)
        cursor.insertFrame(frameFormat)

        cursor.insertText(
            """
                          Documents are either converted from external sources 
                          or created from scratch using Qt. The creation process 
                          can done by an editor widget, such as QTextEdit, or by 
                          explicit calls to the Scribe API.""", boldCharFormat)

        cursor = mainFrame.lastCursorPosition()
        cursor.insertText(
            """
                          There are two complementary ways to visualize the 
                          contents of a document: as a linear buffer that is 
                          used by editors to modify the contents, and as an 
                          object hierarchy containing structural information 
                          that is useful to layout engines. In the hierarchical 
                          model, the objects generally correspond to visual 
                          elements such as frames, tables, and lists. At a lower 
                          level, these elements describe properties such as the 
                          style of text used and its alignment. The linear 
                          representation of the document is used for editing and 
                          manipulation of the document's contents.""",
            plainCharFormat)

        frame = cursor.currentFrame()

        items = []

        #test iterator
        for i in frame:
            items.append(i)

        #test __iadd__
        b = frame.begin()
        i = 0
        while not b.atEnd():
            self.assertEqual(b, items[i])
            self.assertTrue(b.parentFrame(), items[i].parentFrame())
            b.__iadd__(1)
            i += 1

        #test __isub__
        b = frame.end()
        i = 0
        while i > 0:
            self.assertEqual(b, items[i])
            self.assertTrue(b.parentFrame(), items[i].parentFrame())
            b.__isub__(1)
            i -= 1
Esempio n. 6
0
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):

        grid_layout = QGridLayout()
        grid_layout.setSpacing(10)
        self.setLayout(grid_layout)

        #Tutorial
        self.tutorialLabel = QLabel()
        self.tutorialLabel.setText(
            "Welcome to DeepCreamPy!\n\nIf you're new to DCP, please read the README.\nThis program does nothing without the proper setup of your images.\n\nReport any bugs you encounter to me on Github or Twitter @deeppomf."
        )
        self.tutorialLabel.setAlignment(Qt.AlignCenter)
        self.tutorialLabel.setFont(QFont('Sans Serif', 13))

        #Censor type group
        self.censorTypeGroupBox = QGroupBox('Censor Type')

        barButton = QRadioButton('Bar censor')
        mosaicButton = QRadioButton('Mosaic censor')
        barButton.setChecked(True)

        censorLayout = QVBoxLayout()
        censorLayout.addWidget(barButton)
        censorLayout.addWidget(mosaicButton)
        # censorLayout.addStretch(1)
        self.censorTypeGroupBox.setLayout(censorLayout)

        #Variation count group
        self.variationsGroupBox = QGroupBox('Number of Decensor Variations')

        var1Button = QRadioButton('1')
        var2Button = QRadioButton('2')
        var3Button = QRadioButton('4')
        var1Button.setChecked(True)

        varLayout = QVBoxLayout()
        varLayout.addWidget(var1Button)
        varLayout.addWidget(var2Button)
        varLayout.addWidget(var3Button)
        # varLayout.addStretch(1)
        self.variationsGroupBox.setLayout(varLayout)

        #Decensor button
        self.decensorButton = QPushButton('Decensor Your Images')
        self.decensorButton.clicked.connect(self.decensorClicked)
        self.decensorButton.setSizePolicy(QSizePolicy.Preferred,
                                          QSizePolicy.Preferred)

        #Progress message
        # self.progressGroupBox = QGroupBox('Progress')

        self.progressMessage = QTextEdit()
        self.progressCursor = QTextCursor(self.progressMessage.document())
        self.progressMessage.setTextCursor(self.progressCursor)
        self.progressMessage.setReadOnly(True)
        self.progressCursor.insertText(
            "After you prepared your images, click on the decensor button once to begin decensoring.\nPlease be patient.\nDecensoring will take time.\n"
        )

        #put all groups into grid
        grid_layout.addWidget(self.tutorialLabel, 0, 0, 1, 2)
        grid_layout.addWidget(self.censorTypeGroupBox, 1, 0, 1, 1)
        grid_layout.addWidget(self.variationsGroupBox, 1, 1, 1, 1)
        grid_layout.addWidget(self.decensorButton, 2, 0, 1, 2)
        grid_layout.addWidget(self.progressMessage, 3, 0, 4, 2)

        #window size settings
        self.resize(500, 500)
        self.center()
        self.setWindowTitle('DeepCreamPy v2.2.0-beta')
        self.show()

    def decensorClicked(self):
        self.decensorButton.setEnabled(False)
        self.progressMessage.clear()
        self.progressCursor.insertText("Decensoring has begun!\n")

        decensor = Decensor(text_edit=self.progressMessage,
                            text_cursor=self.progressCursor,
                            ui_mode=True)
        #https://stackoverflow.com/questions/42349470/pyqt-find-checked-radiobutton-in-a-group
        #set decensor to right settings
        #censor type
        censorTypeElements = self.censorTypeGroupBox.children()
        censorButtons = [
            elem for elem in censorTypeElements
            if isinstance(elem, QRadioButton)
        ]
        for cb in censorButtons:
            if cb.isChecked():
                censorType = cb.text()
        if censorType == 'Bar censor':
            decensor.is_mosaic = False
        else:
            decensor.is_mosaic = True

        #variations count
        variationsElements = self.variationsGroupBox.children()
        variationsButtons = [
            elem for elem in variationsElements
            if isinstance(elem, QRadioButton)
        ]
        for vb in variationsButtons:
            if vb.isChecked():
                variations = int(vb.text())
        decensor.variations = variations

        self.decensorButton.setEnabled(True)
        self.hide()
        self.progress = ProgressWindow(self, decensor=decensor)
        # decensor.decensor_all_images_in_folder()

        # self.progress.hide()
        # self.show()

    # def showAbout(self):
    # 	QMessageBox.about(self, 'About', "DeepCreamPy v2.2.0 \n Developed by deeppomf")

    # #centers the main window
    def center(self):
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())
Esempio n. 7
0
    def testCase(self):
        editor = QTextEdit()
        cursor = QTextCursor(editor.textCursor())
        cursor.movePosition(QTextCursor.Start)
   
        mainFrame = cursor.currentFrame()
        
        plainCharFormat = QTextCharFormat()
        boldCharFormat = QTextCharFormat()
        boldCharFormat.setFontWeight(QFont.Bold);
        cursor.insertText("""
                          Text documents are represented by the 
                          QTextDocument class, rather than by QString objects. 
                          Each QTextDocument object contains information about 
                          the document's internal representation, its structure, 
                          and keeps track of modifications to provide undo/redo 
                          facilities. This approach allows features such as the 
                          layout management to be delegated to specialized 
                          classes, but also provides a focus for the framework.""",
                          plainCharFormat)

        frameFormat = QTextFrameFormat()
        frameFormat.setMargin(32)
        frameFormat.setPadding(8)
        frameFormat.setBorder(4)
        cursor.insertFrame(frameFormat)

        cursor.insertText("""
                          Documents are either converted from external sources 
                          or created from scratch using Qt. The creation process 
                          can done by an editor widget, such as QTextEdit, or by 
                          explicit calls to the Scribe API.""",
                          boldCharFormat)

        cursor = mainFrame.lastCursorPosition()
        cursor.insertText("""
                          There are two complementary ways to visualize the 
                          contents of a document: as a linear buffer that is 
                          used by editors to modify the contents, and as an 
                          object hierarchy containing structural information 
                          that is useful to layout engines. In the hierarchical 
                          model, the objects generally correspond to visual 
                          elements such as frames, tables, and lists. At a lower 
                          level, these elements describe properties such as the 
                          style of text used and its alignment. The linear 
                          representation of the document is used for editing and 
                          manipulation of the document's contents.""",
                          plainCharFormat)

        
        frame = cursor.currentFrame()

        items = []

        #test iterator
        for i in frame:
            items.append(i)

        #test __iadd__
        b = frame.begin()
        i = 0
        while not b.atEnd():
            self.assertEqual(b, items[i])
            self.assert_(b.parentFrame(), items[i].parentFrame())
            b.__iadd__(1)
            i += 1

        #test __isub__
        b = frame.end()
        i = 0
        while i > 0:
            self.assertEqual(b, items[i])
            self.assert_(b.parentFrame(), items[i].parentFrame())
            b.__isub__(1)
            i -= 1
class PlainTextFindReplaceDialog(QDialog):
    """
    Modeless, stay-above-parent dialog that supports find and replace.

    Allows for searching case (in)sensitively, and whole-word.
    Find triggered by Enter / Shift+Enter, or corresponding button (Next / Previous), or if Replace clicked before
    Next / Previous.
    Find wraps.
    Highlights all matches, operating on the closest-to-user's-cursor selection first,
    in the user-selected direction (Next / Previous).
    Highlighting / found cursors retained on navigation back to text editor, and cleared on re-find/replace if
    user modified the document.
    Presents an info label (e.g. "x of y", "No matches found", ...)

    While no members have a leading underscore, the only explicit public interface is the static method `find_all`.
    I couldn't find a reason to need to interface with anything in here, so I didn't differentiate everything as
    "private".
    """
    def __init__(self, plain_text_edit: QPlainTextEdit, parent=None):
        """
        Sets up the dialog.

        :param plain_text_edit: Text Editor to operate on.
        :param parent: QWidget parent
        """
        super().__init__(parent=parent, f=Qt.Tool)
        self.plain_text_edit = plain_text_edit
        self.cursors_needed = True
        self.find_flags = QTextDocument.FindFlags()
        self.found_cursors: List[QTextCursor] = []
        self.current_cursor = QTextCursor()

        # UI
        layout = QVBoxLayout()
        find_and_replace_layout = QGridLayout()
        layout.addLayout(
            find_and_replace_layout
        )  # if QGL is sub-layout, add to parent layout before doing stuff.

        self.find_line_edit = QLineEdit()
        find_label = QLabel("&Find")
        find_label.setBuddy(self.find_line_edit)

        options_layout = QHBoxLayout()
        self.match_case_check_box = QCheckBox("Match Case")
        self.whole_word_check_box = QCheckBox("Whole Word")
        options_layout.addWidget(self.match_case_check_box)
        options_layout.addWidget(self.whole_word_check_box)
        options_layout.addStretch()

        self.found_info_label = QLabel()

        self.replace_line_edit = QLineEdit()
        replace_label = QLabel("&Replace")
        replace_label.setBuddy(self.replace_line_edit)

        find_and_replace_layout.addWidget(find_label, 0, 0)
        find_and_replace_layout.addWidget(self.find_line_edit, 0, 1)
        find_and_replace_layout.addLayout(options_layout, 1, 1)
        find_and_replace_layout.setRowMinimumHeight(2, 20)
        find_and_replace_layout.addWidget(self.found_info_label, 2, 1)
        find_and_replace_layout.addWidget(replace_label, 3, 0)
        find_and_replace_layout.addWidget(self.replace_line_edit, 3, 1)

        self.btn_box = QDialogButtonBox(QDialogButtonBox.Close)
        self.find_next_btn = QPushButton("Next")
        self.find_next_btn.setEnabled(False)
        self.find_prev_btn = QPushButton("Previous")
        self.find_prev_btn.setEnabled(False)
        self.replace_btn = QPushButton("Replace")
        self.replace_btn.setEnabled(False)
        self.replace_all_btn = QPushButton("Replace All")
        self.replace_all_btn.setEnabled(False)
        self.btn_box.addButton(self.replace_btn, QDialogButtonBox.ActionRole)
        self.btn_box.addButton(self.replace_all_btn,
                               QDialogButtonBox.ActionRole)
        self.btn_box.addButton(self.find_prev_btn, QDialogButtonBox.ActionRole)
        self.btn_box.addButton(self.find_next_btn, QDialogButtonBox.ActionRole)

        layout.addWidget(self.btn_box)
        self.setLayout(layout)
        # End UI

        self.btn_box.rejected.connect(self.reject)
        self.find_line_edit.textEdited.connect(self.handle_text_edited)
        self.find_next_btn.clicked.connect(self.next)
        self.find_prev_btn.clicked.connect(self.prev)
        self.replace_btn.clicked.connect(self.replace)
        self.replace_all_btn.clicked.connect(self.replace_all)
        self.plain_text_edit.document().contentsChanged.connect(
            self.set_cursors_needed_true)
        self.whole_word_check_box.stateChanged.connect(
            self.toggle_whole_word_flag)
        self.match_case_check_box.stateChanged.connect(
            self.toggle_match_case_flag)

    # SLOTS
    def next(self):
        """
        Finds all matches to the user's search. First highlight after cursor. Consecutive calls advance through matches.

        If there are matches or not, it says so. The user's cursor advances along with the current selection. Loops back
        to start after the last match.
        :return: Side effect: Highlights all matches, differentiating (maybe moving fwd) the current selection.
        """
        if self.cursors_needed:
            self.init_find()

        if not self.found_cursors:
            self.found_info_label.setText("No matches found")
            self.found_info_label.repaint()
            return

        if self.current_cursor >= self.found_cursors[
                -1]:  # loop back to start. cursor equality based on position.
            self.current_cursor = self.found_cursors[0]
        else:
            for cur in self.found_cursors:
                if cur > self.current_cursor:  # next in order
                    self.current_cursor = cur
                    break

        self.update_visuals()

    def prev(self):
        """
        Finds all matches to user's search. First highlight before cursor. Consecutive calls retreat through matches.

        If there are matches or not, it says so. The user's cursor moves along with the current selection. Loops back
        to end after the last (first in doc) match.
        :return: Side effect: Highlights all matches, differentiating (maybe moving back) the current selection.
        """
        if self.cursors_needed:
            self.init_find()

        if not self.found_cursors:
            self.found_info_label.setText("No matches found")
            self.found_info_label.repaint()
            return

        if self.current_cursor <= self.found_cursors[0]:  # loop back to end.
            self.current_cursor = self.found_cursors[-1]
        else:
            for cur in reversed(self.found_cursors):
                if cur < self.current_cursor:  # prev in order
                    self.current_cursor = cur
                    break

        self.update_visuals()

    def replace(self):
        """
        Replaces the word under focus by `next`.

        Replaces with the Replace line edit's text, and advances to next word. If no word under focus via this dialog,
        calls `next`.
        :return: Side effect: replaces word in text edit
        """
        if self.cursors_needed:
            self.next()
            return

        if not self.found_cursors:
            return

        self.plain_text_edit.document().contentsChanged.disconnect(
            self.set_cursors_needed_true)  # don't dup work.
        self.current_cursor.insertText(self.replace_line_edit.text())
        self.plain_text_edit.document().contentsChanged.connect(
            self.set_cursors_needed_true)
        self.found_cursors.remove(self.current_cursor)
        self.next()

    def replace_all(self):
        """
        Replaces all instances of Find's text with Replace's text.

        :return: Side effect: replaces words in text edit. Indicates success to user via info label on dialog.
        """
        if self.cursors_needed:
            self.init_find()

        for cur in self.found_cursors:
            cur.insertText(self.replace_line_edit.text())

        self.found_info_label.setText("Made {} replacements".format(
            len(self.found_cursors)))
        self.found_info_label.repaint()

    def handle_text_edited(self, text):
        """
        Modifies button states, clears info text, and sets self.cursors_needed to True.

        :param text: The find_line_edit's text.
        :return: Side effect: btn enabled / default
        """
        self.found_info_label.clear()

        self.cursors_needed = True

        find_enabled = text != ""
        self.find_next_btn.setEnabled(find_enabled)
        self.find_prev_btn.setEnabled(find_enabled)
        self.replace_btn.setEnabled(find_enabled)
        self.replace_all_btn.setEnabled(find_enabled)

        self.find_next_btn.setDefault(find_enabled)
        self.btn_box.button(
            QDialogButtonBox.Close).setDefault(not find_enabled)

    def set_cursors_needed_true(self):
        self.cursors_needed = True

    def toggle_match_case_flag(self, state: int):
        self.found_info_label.clear(
        )  # User will be performing a new search upon toggle, so want this reset.
        self.cursors_needed = True

        if state == Qt.Unchecked:
            self.find_flags &= ~QTextDocument.FindCaseSensitively
        elif state == Qt.Checked:
            self.find_flags |= QTextDocument.FindCaseSensitively

    def toggle_whole_word_flag(self, state: int):
        self.found_info_label.clear(
        )  # User will be performing a new search upon toggle, so want this reset.
        self.cursors_needed = True

        if state == Qt.Unchecked:
            self.find_flags &= ~QTextDocument.FindWholeWords
        elif state == Qt.Checked:
            self.find_flags |= QTextDocument.FindWholeWords

    # END SLOTS

    def init_find(self):
        """Sets up internal state for the case when cursors are needed (e.g. first find, user modifies doc...)"""
        self.found_cursors = self.find_all(self.find_line_edit.text(),
                                           self.plain_text_edit.document(),
                                           self.find_flags)
        self.cursors_needed = False
        self.current_cursor = self.plain_text_edit.textCursor(
        )  # returns copy of
        self.plain_text_edit.setExtraSelections([])

    def update_visuals(self):
        """
        Moves text editor's cursor to match currently highlighted `find` word, performs `find` highlighting,
        indicates index on dialog.
        """
        # x of y words indicator
        idx = self.found_cursors.index(self.current_cursor) + 1
        self.found_info_label.setText("{} of {} matches".format(
            idx, len(self.found_cursors)))
        self.found_info_label.repaint()

        # move along text editor's viewport
        next_pte_cursor = QTextCursor(self.current_cursor)
        next_pte_cursor.clearSelection()
        self.plain_text_edit.setTextCursor(next_pte_cursor)

        #highlighting
        normal_color = QColor(Qt.yellow).lighter()
        current_color = QColor(Qt.magenta).lighter()
        extra_selections: List[QTextEdit.ExtraSelection] = []
        for cur in self.found_cursors:
            selection = QTextEdit.ExtraSelection()
            selection.cursor = cur
            if cur == self.current_cursor:
                selection.format.setBackground(current_color)
            else:
                selection.format.setBackground(normal_color)
            extra_selections.append(selection)
        self.plain_text_edit.setExtraSelections(extra_selections)

    @staticmethod
    def find_all(
        text: str, document: QTextDocument, flags=QTextDocument.FindFlags()
    ) -> List[QTextCursor]:
        """
        Finds all occurrences of `text` in `document`, in order.

        :param text: Text to find.
        :param document: Document to search.
        :param flags: Conditions to set on the search: none or (whole word and/or match case)
        :return: Ordered list of all found instances.
        """
        cursor = QTextCursor(document)  # default pos == 0
        found: List[QTextCursor] = []

        while True:
            cursor = document.find(text, cursor, flags)
            if cursor.isNull():
                return found
            else:
                found.append(cursor)

    def done(self, arg__1: int):
        self.plain_text_edit.setExtraSelections([])
        super().done(arg__1)

    def keyPressEvent(self, arg__1: QKeyEvent):
        # Shift+Enter triggers find previous, if the corresponding btn is enabled.
        if (arg__1.key() in [Qt.Key_Return, Qt.Key_Enter]
                and arg__1.modifiers() == Qt.ShiftModifier
                and self.find_prev_btn.isEnabled()):
            self.prev()
        else:
            super().keyPressEvent(arg__1)