Beispiel #1
0
 def start_gui(self, args):
     crypt_key = args.encryption
     if crypt_key is None:
         input_dialog = QInputDialog()
         input_dialog.setInputMode(QInputDialog.TextInput)
         input_dialog.setCancelButtonText("No Password")
         input_dialog.setLabelText("Please enter your Masterpassword")
         input_dialog.setWindowTitle("Masterpassword")
         input_dialog.setModal(False)
         while crypt_key is None:
             if input_dialog.exec_() == QInputDialog.Rejected:
                 crypt_key = u"0238jh74ngz23v84m90fcqewmn4f89"
             else:
                 crypt_key = input_dialog.textValue().strip()
             if not QtStarter.check_master_password(crypt_key):
                 crypt_key = None
                 input_dialog.setLabelText("Wrong Masterpassword, try again!")
     QtStarter.save_masterpassword_check(crypt_key)
     crypt = Coding(crypt_key.encode('utf-8'))
     self.window = ControlMainWindow(crypt)
     self.timer = threading.Timer(0.001, self.update_server_status,
                                  kwargs={'window': self.window}).start()
     self.window.show()
     ret = self.app.exec_()
     if self.timer is not None:
         self.timer.cancel()
     return ret
Beispiel #2
0
    def AddBookFromURL(self):
        # Create Resized Input Dialog
        dlg = QInputDialog()
        dlg.setInputMode(QInputDialog.TextInput)
        dlg.setLabelText('Enter URL:')
        dlg.setTextValue('https://www.readlightnovel.org/only-i-level-up')
        dlg.setWindowTitle('Add Novel')
        dlg.resize(500, 100)

        # If Cancelled
        if dlg.exec_() == 0:
            return

        # Get URL
        url = dlg.textValue()

        # Check URL
        if self.settings['MainURL'] not in url:
            print('{0} not in {1}'.format(self.settings['MainURL'], url))
            return

        # Dump Info
        try:
            info = Utils.dumpInfo(url, self.settings)
        except AttributeError:
            print('Incorrect URL')
            return

        # Dump Cover
        Utils.dumpCover(info, self.settings)

        self.UpdateList()
Beispiel #3
0
    async def attempt_join(self):
        if not self.visible_sessions:
            return

        session = self.selected_session

        if session.has_password:
            dialog = QInputDialog(self)
            dialog.setWindowTitle("Enter password")
            dialog.setLabelText("This session requires a password:"******"Incorrect Password",
                                       "The password entered was incorrect.")
 def add_rule(self):
     input = QInputDialog(self.window)
     input.setWindowTitle("Add exclude rule")
     input.setLabelText("Exclude rule (eg: C:\\Users\\*\\Volumes\\*.iso)")
     if input.exec_():
         exclude_rule = input.textValue()
         exclude_rule = exclude_rule.replace(os.sep, "/")
         self.exclude_rules.add(exclude_rule)
         self.reload_exclude_rules()
Beispiel #5
0
    def get_text(parent=None,
                 title='Input Text Value',
                 label='Value:',
                 default_value=None):
        input_dialog = QInputDialog(parent)
        input_dialog.setInputMode(InputDialog.TextInput)
        input_dialog.setWindowTitle(title)
        input_dialog.setLabelText(label)
        input_dialog.setOkButtonText('确定')
        input_dialog.setCancelButtonText('取消')
        if parent:
            input_dialog.setFont(parent.font())

        return default_value if not input_dialog.exec_(
        ) else input_dialog.textValue()
Beispiel #6
0
    def get_charname(self):
        """
        :param mailurl: url to call for sending an authcode per mail
        :return: the authcode
        """
        inputDialog = QInputDialog(self)
        inputDialog.setInputMode(QInputDialog.TextInput)
        inputDialog.setLabelText("Please enter a Charname")
        inputDialog.setWindowTitle("Charname Challange")
        inputDialog.setModal(True)

        if inputDialog.exec_() == QInputDialog.Rejected:
            return None

        return inputDialog.textValue().strip()
Beispiel #7
0
    def get_int(parent=None,
                title='Input Int Value',
                label='Value:',
                default_value=None,
                minimum=1,
                maximum=100):
        input_dialog = QInputDialog(parent)
        input_dialog.setInputMode(InputDialog.IntInput)
        input_dialog.setWindowTitle(title)
        input_dialog.setLabelText(label)
        input_dialog.setOkButtonText('确定')
        input_dialog.setCancelButtonText('取消')
        input_dialog.setIntRange(minimum, maximum)
        input_dialog.setIntValue(default_value or minimum)
        if parent:
            input_dialog.setFont(parent.font())

        return default_value if not input_dialog.exec_(
        ) else input_dialog.intValue()
Beispiel #8
0
    async def _host_game_session(self):
        if await self._game_session_active():
            return

        if not await self._ensure_logged_in():
            return

        dialog = QInputDialog(self)
        dialog.setModal(True)
        dialog.setWindowTitle("Enter session name")
        dialog.setLabelText("Select a name for the session:")
        if await async_dialog.execute_dialog(dialog) != dialog.Accepted:
            return

        await self.network_client.create_new_session(dialog.textValue())
        self.game_session_window = await GameSessionWindow.create_and_update(
            self.network_client, common_qt_lib.get_game_connection(),
            self.preset_manager, self, self._options)
        if self.game_session_window is not None:
            self.game_session_window.show()
Beispiel #9
0
    def get_double(parent=None,
                   title='Input Double Value',
                   label='Value:',
                   default_value=None,
                   decimals=1,
                   minimum=1,
                   maximum=1e9):
        input_dialog = QInputDialog(parent)
        input_dialog.setInputMode(InputDialog.DoubleInput)
        input_dialog.setWindowTitle(title)
        input_dialog.setLabelText(label)
        input_dialog.setOkButtonText('确定')
        input_dialog.setCancelButtonText('取消')
        input_dialog.setDoubleDecimals(decimals)
        input_dialog.setDoubleRange(minimum, maximum)
        input_dialog.setDoubleValue(default_value or minimum)
        if parent:
            input_dialog.setFont(parent.font())

        return default_value if not input_dialog.exec_(
        ) else input_dialog.doubleValue()
Beispiel #10
0
def askGenericText(message, title, parent=None, previous=""):
    """Uses `QInputDialog` to ask a text answer for a given question.

    Parameters:
        message: the question to be displayed
        title: the window title
        parent (optional, default None): the parent of the window

    Output:
        return a tuple containing the text as first element
        and True/False
            (depending if the user selected "Ok" or "Cancel")
            as the second element
    """
    dialog = QInputDialog(parent)
    dialog.setInputMode(QInputDialog.TextInput)
    dialog.setWindowTitle(title)
    dialog.setLabelText(message)
    dialog.setTextValue(previous)
    out = dialog.exec_()
    return dialog.textValue(), out
    def _renameItem(self):
        item = self._list.currentItem()
        
        input_dialog = QInputDialog(self.parentWidget(), Qt.WindowSystemMenuHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint)
        font = input_dialog.font()
        font.setPixelSize(16)
        input_dialog.setFont(font)
        input_dialog.setMinimumWidth(300)
        input_dialog.setInputMode(QInputDialog.TextInput)
        input_dialog.setWindowTitle("Renommer l'analyse")
        input_dialog.setLabelText("Nouveau nom pour '%s' :" % item.data(Qt.UserRole + 1))
        input_dialog.setTextValue(item.data(Qt.UserRole + 1))
        input_dialog.setOkButtonText("OK")
        input_dialog.setCancelButtonText("Annuler")

        if not input_dialog.exec_():
            return
        
        new_name = input_dialog.textValue()

        if self._analysis is None:
            return

        if new_name == self._analysis.parameters().name():
            return
        
        regexp = QRegExp("^[a-zA-Z0-9_-#éèêëàîï ]{5,30}$")
        
        if not regexp.exactMatch(new_name):
            QMessageBox.warning(self, "Nouveau nom invalide", "Caractères autorisés : alphanumérique, espace, #, - et _ avec une longueur maximale de 30 caractères")
            return


        self._analysis.parameters().setName(new_name)
        HistoryManager.renameAnalysis(item.data(Qt.UserRole), self._analysis)
        
        current_row = self._list.currentRow()
        self.reset(self._analysis)
        self._list.setCurrentRow(current_row)
        self.currentAnalysisChanged.emit(self._analysis)
Beispiel #12
0
    def get_auth_code(self, opener, request):
        """
        :param opener: urllib.request.build_opener for sending an authcode per mail
        :param request: request to send using the given opener
        :return: the authcode
        """
        inputDialog = QInputDialog(self)
        inputDialog.setInputMode(QInputDialog.TextInput)
        inputDialog.setCancelButtonText("Cancel")
        inputDialog.setLabelText("Please enter your Authcode")
        inputDialog.setWindowTitle("TwoFactorAuth")
        inputDialog.setModal(True)

        response = None

        if inputDialog.exec_() == QInputDialog.Rejected:  # send mail
            return None, None
        else:
            return response, inputDialog.textValue().strip()

        inputDialog.setCancelButtonText("Cancel")
        if inputDialog.exec_() == QInputDialog.Rejected:
            return response, None
        return response, inputDialog.textValue().strip()
def snip(bounding_box=None,
         file_name_pattern=DEFAULT_OUTPUT_FILE_NAME_PATTERN,
         override=False,
         show_time=1000,
         assign_description=0):
    """ Snip screen image and save it to file.

    :param bounding_box: [tuple] The image rectangle in screen, formatted in (left, upper, width, height).
    :param file_name_pattern: [string] The file name pattern (absolute path or relative path to this python script file). For example: "ScreenSnippingImages/ScreenSnippingImage_%Y-%m-%d_%H-%M-%S.png".
    :param override: [bool] Whether to override the output file if it exists before.
    :param show_time: [int] Milliseconds time to show the screen image (if 0, the image won't be shown).
    :param assign_description: [int] Whether to assign description to the screen image (1 for manually input, 2 for OCR, others for no description).
    """

    logging.info("Started snipping screen.")

    screen_image = get_screen_image(bounding_box=bounding_box)
    file_name = save_image(image=screen_image,
                           file_name_pattern=file_name_pattern,
                           override=override)

    if file_name:
        logging.info(
            "Screen image has been saved in file: {}".format(file_name))

        if show_time > 0:
            image_dialog = QDialog()
            image_dialog.setWindowTitle(APP_DESCRIPTION + " " + APP_VERSION)
            image_dialog.setWindowFlags(Qt.WindowStaysOnTopHint)
            image_dialog.setFixedSize(640, 360)
            image_dialog.setContentsMargins(0, 0, 0, 0)
            image_label = QLabel()
            image_dialog_layout = QGridLayout(image_dialog)
            image_dialog_layout.setContentsMargins(0, 0, 0, 0)
            image_dialog_layout.addWidget(image_label)
            image_label.setPixmap(screen_image)
            image_label.setScaledContents(True)
            QTimer().singleShot(10, image_dialog.activateWindow)
            QTimer().singleShot(show_time, image_dialog.close)
            image_dialog.exec()

            if assign_description == 1:
                description_input_dialog = QInputDialog()
                description_input_dialog.setWindowTitle(APP_DESCRIPTION + " " +
                                                        APP_VERSION)
                description_input_dialog.setWindowFlags(
                    Qt.WindowStaysOnTopHint)
                description_input_dialog.setFixedSize(400, 200)
                description_input_dialog.setInputMode(
                    description_input_dialog.TextInput)
                description_input_dialog.setLabelText(
                    "Please input description:")
                QTimer().singleShot(10,
                                    description_input_dialog.activateWindow)
                description_input_dialog.exec()
                description = description_input_dialog.textValue()
                if description:
                    description_file_name = file_name + ".txt"
                    with open(description_file_name, "w") as file:
                        file.write(description)
                    logging.info(
                        "Assigned a description for screen image file: {}".
                        format(file_name))
                    logging.debug("Description: {}".format(description))
            elif assign_description == 2:
                import pytesseract
                text_from_image = pytesseract.image_to_string(
                    Image.open(file_name))
                description_file_name = file_name + "-OCR.txt"
                with open(description_file_name, "w") as file:
                    file.write(text_from_image)
                os.startfile(description_file_name)
            else:
                pass
    else:
        logging.error("Error occurred.")
Beispiel #14
0
    def GenerateBook(self):
        wdgList = self.ui.wdgList

        # Get Selected List
        selected = [
            item.text().split(' | ') for item in wdgList.selectedItems()
        ]

        if len(selected) <= 0:
            return

        # Dump not downloaded chapters
        notDumped = [x for x in selected if x[1][-1] != ']']

        # Download chapters
        if not self.downloadChapters(notDumped):
            self.UpdateList()
            # If canceled stop epub generation
            return

        self.UpdateList()

        # Update selected list
        selected = [[x[0][1:], x[1][:-1]] for x in selected if x[1][-1] == ']']
        selected += notDumped

        # Get dicts from Info dictionary
        chapters = [
            chp for chp in self.info['chapters'] for volume, name in selected
            if name == chp['name'] and volume == chp['volume']
        ]

        # Generate Title for book
        volume = chapters[0]['volume']
        for chapter in chapters:
            if chapter['volume'] != volume or 'chapters' in volume.lower():
                volume = 'Chapters:'

        title = self.info['title'] + ' ' + volume

        try:
            # If chapters are not from the same volume
            if volume == 'Chapters:':
                numbers = [
                    int(
                        float(''.join(s for s in name
                                      if s.isdigit() or s == '.' or s == '-')))
                    for name in selected
                ]
                numbers = list(set(numbers))

                # Generate ranges for number list
                ranges = Utils.ranges(numbers)

                for r in ranges:
                    if r[0] == r[1]:
                        title += ' {0},'.format(r[0])
                    else:
                        title += ' {0} - {1},'.format(r[0], r[1])

                if title[-1] == ',':
                    title = title[:-1]
        except ValueError:
            pass

        # Show Title Input Dialog
        dlg = QInputDialog()
        dlg.setInputMode(QInputDialog.TextInput)
        dlg.setLabelText('Enter Title:')
        dlg.setWindowTitle('Select Book Title')
        dlg.setTextValue(title)
        dlg.resize(500, 100)

        # If Cancelled
        if dlg.exec_() == 0:
            return

        # Get URL
        title = dlg.textValue()

        # Show File Dialog
        ans = QFileDialog.getSaveFileName(caption='Save ePUB',
                                          filter='ePUB (*.epub)',
                                          dir=title + '.epub')
        filename = ans[0]

        # Add file format if not present
        if filename[-5:] != '.epub':
            filename += '.epub'

        # Generate ePUB file
        Utils.generateEPUB(filename, title, self.info, chapters, self.settings)
Beispiel #15
0
class App(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.attach_event()
        self.setAcceptDrops(True)
        
    def init_ui(self):
        # Define & Configure Components
        normal_button_size = QSize(80, 24)
        icon_button_size = QSize(24, 24)
        icon_size = QSize(18, 18)
        
        self.central_widget = QWidget()
        self.central_layout = QGridLayout()
        self.central_widget.setLayout(self.central_layout)

        self.tab_group_widget = QTabWidget()
        self.tab_group_widget.setMinimumSize(400, 0)
        self.tab_group_widget.setFixedHeight(150)
        self.tab1_name = '스폰서 변환'
        self.tab2_name = '싱크 조절(초)'
        self.tab3_name = '싱크 조절(%)'

        self.tab_page_1 = QWidget()
        self.tab_grid_1 = QGridLayout()
        self.tab1_search_label = QLabel('검색 텍스트')
        self.tab1_search = QLineEdit()
        self.tab1_sponsor = QWidget()
        self.tab1_sponsor_layout = QHBoxLayout()
        self.tab1_sponsor_layout.setContentsMargins(0, 0, 0, 0)
        self.tab1_sponsor_label = QLabel('스폰서 영상 길이')
        self.tab1_sponsor_value = QDoubleSpinBox()
        self.tab1_sponsor_value.setFixedWidth(60)
        self.tab1_sponsor_value.setMinimum(-1000000000)
        self.tab1_sponsor_value.setValue(10)
        self.tab1_offset = QWidget()
        self.tab1_offset_layout = QHBoxLayout()
        self.tab1_offset_layout.setContentsMargins(0, 0, 0, 0)
        self.tab1_offset_label = QLabel('라인 오프셋')
        self.tab1_offset_value = QSpinBox()
        self.tab1_offset_value.setMinimum(-1000000000)
        self.tab1_offset_value.setValue(2)
        self.tab1_offset_value.setFixedWidth(50)
        self.tab1_ignore = QWidget()
        self.tab1_ignore_layout = QHBoxLayout()
        self.tab1_ignore_layout.setContentsMargins(0, 0, 0, 0)
        self.tab1_ignore_label1 = QLabel('시작부터')
        self.tab1_ignore_value = QSpinBox()
        self.tab1_ignore_value.setFixedWidth(50)
        self.tab1_ignore_value.setValue(5)
        self.tab1_ignore_label2 = QLabel('줄 ')
        self.tab1_ignore_sec = QSpinBox()
        self.tab1_ignore_sec.setFixedWidth(60)
        self.tab1_ignore_sec.setMaximum(1000)
        self.tab1_ignore_sec.setValue(90)
        self.tab1_ignore_label3 = QLabel('초 무시하기')
        self.tab1_add_button = QPushButton('추가하기')

        self.tab_page_2 = QWidget()
        self.tab_grid_2 = QGridLayout()
        self.tab2_shift = QWidget()
        self.tab2_shift_layout = QHBoxLayout()
        self.tab2_shift_layout.setContentsMargins(0, 0, 0, 0)
        self.tab2_shift_label1 = QLabel('자막 싱크')
        self.tab2_shift_value = QDoubleSpinBox()
        self.tab2_shift_value.setFixedWidth(60)
        self.tab2_shift_label2 = QLabel('초 ')
        self.tab2_slow_radio = QRadioButton('느리게')
        self.tab2_slow_radio.setChecked(True)
        self.tab2_fast_radio = QRadioButton('빠르게')
        self.tab2_add_button = QPushButton('추가하기')

        self.tab_page_3 = QWidget()
        self.tab_grid_3 = QGridLayout()
        self.tab3_speed_label1 = QLabel('자막 싱크')
        self.tab3_speed_value = QSpinBox()
        self.tab3_speed_value.setFixedWidth(70)
        self.tab3_speed_value.setRange(1, 1000)
        self.tab3_speed_value.setValue(100)
        self.tab3_speed_label2 = QLabel('%')
        self.tab3_add_button = QPushButton('추가하기')

        self.que_label = QLabel('작업 목록')
        self.que_label.setFixedHeight(24)
        self.que_widget = QWidget()
        self.que_widget.setFixedHeight(114)
        self.que_layout = QGridLayout()
        self.que_layout.setContentsMargins(0, 0, 0, 0)
        self.que_list = QTreeWidget()
        self.que_list.setHeaderLabels(['작업', '옵션'])
        self.que_delete_button = QPushButton(QIcon(':/remove.png'), '')
        self.que_delete_button.setFixedSize(icon_button_size)
        self.que_delete_button.setIconSize(icon_size)
        self.que_delete_button.setToolTip('목록 삭제')
        self.que_up_button = QPushButton(QIcon(':/up.png'), '')
        self.que_up_button.setIconSize(icon_size)
        self.que_up_button.setFixedSize(icon_button_size)
        self.que_up_button.setToolTip('위로')
        self.que_down_button = QPushButton(QIcon(':/down.png'), '')
        self.que_down_button.setIconSize(icon_size)
        self.que_down_button.setFixedSize(icon_button_size)
        self.que_down_button.setToolTip('아래로')
        self.que_clear_button = QPushButton(QIcon(':/clear.png'), '')
        self.que_clear_button.setIconSize(icon_size)
        self.que_clear_button.setFixedSize(icon_button_size)
        self.que_clear_button.setToolTip('비우기')

        self.file_label = QLabel('파일 목록')
        self.file_label.setFixedHeight(24)
        self.file_widget = QWidget()
        self.file_layout = QGridLayout()
        self.file_layout.setContentsMargins(0, 0, 0, 0)
        self.file_list = QTreeWidget()
        self.file_list.setAcceptDrops(True)
        self.file_list.setHeaderLabels(['이름', '경로'])
        self.file_file_open = QPushButton(QIcon(':/file.png'), '')
        self.file_file_open.setFixedSize(icon_button_size)
        self.file_file_open.setIconSize(icon_size)
        self.file_file_open.setToolTip('파일 열기')
        self.file_dir_open = QPushButton(QIcon(':/folder.png'), '')
        self.file_dir_open.setFixedSize(icon_button_size)
        self.file_dir_open.setIconSize(icon_size)
        self.file_dir_open.setToolTip('폴더 열기')
        self.file_delete = QPushButton(QIcon(':/remove.png'), '')
        self.file_delete.setFixedSize(icon_button_size)
        self.file_delete.setIconSize(icon_size)
        self.file_delete.setToolTip('목록 삭제')
        self.file_clear = QPushButton(QIcon(':/clear.png'), '')
        self.file_clear.setFixedSize(icon_button_size)
        self.file_clear.setIconSize(icon_size)
        self.file_clear.setToolTip('비우기')
        self.file_encode = QPushButton(QIcon(':/encode.png'), '')
        self.file_encode.setFixedSize(icon_button_size)
        self.file_encode.setIconSize(icon_size)
        self.file_encode.setToolTip('인코딩 설정')

        self.save_widget = QGroupBox('저장 옵션')
        self.save_widget.setMinimumSize(400, 0)
        self.save_widget.setFixedHeight(82)
        self.save_layout = QGridLayout()
        self.save_orig_radio = QRadioButton('원본 위치에 저장')
        self.save_orig_radio.setChecked(True)
        self.save_strip = QCheckBox('싱크 꼬임 무시')
        self.save_strip.setToolTip('싱크 꼬임을 무시하고 모든 자막을 보존합니다.')
        self.save_dir_radio = QRadioButton('다른 위치에 저장')
        self.save_dir_line = QLineEdit()
        self.save_dir_find = QPushButton('...')
        self.save_dir_find.setFixedWidth(40)

        self.ok_button = QPushButton('적용')
        self.ok_button.setFixedSize(normal_button_size)
        self.cancel_button = QPushButton('취소')
        self.cancel_button.setFixedSize(normal_button_size)

        # Display GUI Components
        self.central_layout.addWidget(self.tab_group_widget, 0, 0, 1, 3)
        self.central_layout.addWidget(self.que_label, 1, 0, 1, 3)
        self.central_layout.addWidget(self.que_widget, 2, 0, 1, 3)
        self.central_layout.addWidget(self.file_label, 3, 0, 1, 3)
        self.central_layout.addWidget(self.file_widget, 4, 0, 1, 3)
        self.central_layout.addWidget(self.save_widget, 5, 0, 1, 3)
        self.central_layout.addWidget(self.ok_button, 6, 1, 1, 1)
        self.central_layout.addWidget(self.cancel_button, 6, 2, 1, 1)

        self.tab_group_widget.addTab(self.tab_page_1, QIcon(), self.tab1_name)
        self.tab_group_widget.addTab(self.tab_page_2, QIcon(), self.tab2_name)
        self.tab_group_widget.addTab(self.tab_page_3, QIcon(), self.tab3_name)

        self.tab_page_1.setLayout(self.tab_grid_1)
        self.tab_grid_1.addWidget(self.tab1_search_label, 0, 0, 1, 1)
        self.tab_grid_1.addWidget(self.tab1_search, 0, 1, 1, 2)
        self.tab_grid_1.addWidget(self.tab1_sponsor, 1, 1, 1, 1)
        self.tab_grid_1.addWidget(self.tab1_offset, 1, 2, 1, 1)
        self.tab_grid_1.addWidget(self.tab1_ignore, 2, 1, 1, 2)
        self.tab_grid_1.addWidget(self.tab1_add_button, 3, 0, 1, 3)
        self.tab1_sponsor.setLayout(self.tab1_sponsor_layout)
        self.tab1_sponsor_layout.addWidget(self.tab1_sponsor_label)
        self.tab1_sponsor_layout.addWidget(self.tab1_sponsor_value)
        self.tab1_sponsor_layout.addStretch(1)
        self.tab1_offset.setLayout(self.tab1_offset_layout)
        self.tab1_offset_layout.addWidget(self.tab1_offset_label)
        self.tab1_offset_layout.addWidget(self.tab1_offset_value)
        self.tab1_offset_layout.addStretch(1)
        self.tab1_ignore.setLayout(self.tab1_ignore_layout)
        self.tab1_ignore_layout.addWidget(self.tab1_ignore_label1)
        self.tab1_ignore_layout.addWidget(self.tab1_ignore_value)
        self.tab1_ignore_layout.addWidget(self.tab1_ignore_label2)
        self.tab1_ignore_layout.addWidget(self.tab1_ignore_sec)
        self.tab1_ignore_layout.addWidget(self.tab1_ignore_label3)
        self.tab1_ignore_layout.addStretch(1)

        self.tab_page_2.setLayout(self.tab_grid_2)
        self.tab_grid_2.setRowStretch(0, 1)
        self.tab_grid_2.addWidget(self.tab2_shift, 1, 0, 2, 1)
        self.tab_grid_2.addWidget(self.tab2_slow_radio, 1, 1, 1, 1)
        self.tab_grid_2.addWidget(self.tab2_fast_radio, 2, 1, 1, 1)
        self.tab_grid_2.setColumnStretch(2, 1)
        self.tab_grid_2.setRowStretch(3, 1)
        self.tab_grid_2.addWidget(self.tab2_add_button, 4, 0, 1, 3)
        self.tab2_shift.setLayout(self.tab2_shift_layout)
        self.tab2_shift_layout.addWidget(self.tab2_shift_label1)
        self.tab2_shift_layout.addWidget(self.tab2_shift_value)
        self.tab2_shift_layout.addWidget(self.tab2_shift_label2)

        self.tab_page_3.setLayout(self.tab_grid_3)
        self.tab_grid_3.setRowStretch(0, 1)
        self.tab_grid_3.addWidget(self.tab3_speed_label1, 1, 0, 1, 1)
        self.tab_grid_3.addWidget(self.tab3_speed_value, 1, 1, 1, 1)
        self.tab_grid_3.addWidget(self.tab3_speed_label2, 1, 2, 1, 1)
        self.tab_grid_3.setColumnStretch(3, 1)
        self.tab_grid_3.setRowStretch(2, 1)
        self.tab_grid_3.addWidget(self.tab3_add_button, 3, 0, 1, 4)

        self.que_widget.setLayout(self.que_layout)
        self.que_layout.addWidget(self.que_list, 0, 0, 4, 1)
        self.que_layout.addWidget(self.que_delete_button, 0, 1, 1, 1)
        self.que_layout.addWidget(self.que_up_button, 1, 1, 1, 1)
        self.que_layout.addWidget(self.que_down_button, 2, 1, 1, 1)
        self.que_layout.addWidget(self.que_clear_button, 3, 1, 1, 1)

        self.file_widget.setLayout(self.file_layout)
        self.file_layout.addWidget(self.file_list, 0, 0, 6, 1)
        self.file_layout.addWidget(self.file_file_open, 0, 1, 1, 1)
        self.file_layout.addWidget(self.file_dir_open, 1, 1, 1, 1)
        self.file_layout.addWidget(self.file_delete, 2, 1, 1, 1)
        self.file_layout.addWidget(self.file_clear, 3, 1, 1, 1)
        self.file_layout.addWidget(self.file_encode, 5, 1, 1, 1)

        self.save_widget.setLayout(self.save_layout)
        self.save_layout.addWidget(self.save_orig_radio, 0, 0, 1, 1)
        self.save_layout.setColumnStretch(1, 1)
        self.save_layout.addWidget(self.save_strip, 0, 2, 1, 2)
        self.save_layout.addWidget(self.save_dir_radio, 1, 0, 1, 1)
        self.save_layout.addWidget(self.save_dir_line, 1, 1, 1, 2)
        self.save_layout.addWidget(self.save_dir_find, 1, 3, 1, 1)

        self.setWindowTitle('Batch SAMI Sync v0.2')
        self.setCentralWidget(self.central_widget)
        self.adjustSize()

    def attach_event(self):
        # Default encoding hack
        self.encoding = '자동'

        # Define and Connect event handlers
        def tab1_add():
            sponsor_text = self.tab1_search.text()
            sponsor_time = self.tab1_sponsor_value.value()
            line_offset = self.tab1_offset_value.value()
            line_ignore = self.tab1_ignore_value.value()
            time_ignore = self.tab1_ignore_sec.value()
            data = [1, sponsor_time, sponsor_text, line_offset, line_ignore, time_ignore]
            item = QTreeWidgetItem(self.que_list, [self.tab1_name, '스폰서 영상 시간 : ' + str(sponsor_time) + '초, 오프셋 : ' + str(line_offset) + '줄, 시작부터 ' + str(line_ignore) + '번째 줄, ' + str(time_ignore) + '초 무시 - 검색어 : ' + sponsor_text])
            item.setData(2, 2, data)

        def tab2_add():
            shift_time = self.tab2_shift_value.value()
            shift_direction = self.tab2_fast_radio.isChecked()
            direction_text = '빠르게' if shift_direction else '느리게'
            data = [2, shift_time, shift_direction]
            item = QTreeWidgetItem(self.que_list, [self.tab2_name, '자막 싱크 ' + str(shift_time) + '초 ' + direction_text])
            item.setData(2, 2, data)

        def tab3_add():
            speed_rate = self.tab3_speed_value.value()
            data = [3, speed_rate]
            item = QTreeWidgetItem(self.que_list, [self.tab3_name, '자막 속도 ' + str(speed_rate) + '%'])
            item.setData(2, 2, data)

        def file_open():
            selected = QFileDialog.getOpenFileNames(self, "자막 파일 선택", "", "SAMI Files (*.smi);;All Files (*)")
            for file in selected[0]:
                name = ntpath.basename(file)
                Utils.insert_list(self.file_list, name, file)

        def dir_open():
            selected = QFileDialog.getExistingDirectory(self, "자막 폴더 선택")
            for paths, subdirs, files in os.walk(selected):
                for file in files:
                    if fnmatch(file, '*.smi'):
                        name = ntpath.basename(file)
                        Utils.insert_list(self.file_list, name, file)

        def open_encode_dialog():
            self.dialog = QInputDialog(self)
            self.dialog.setWindowTitle('인코딩 설정')
            self.dialog.setLabelText('텍스트 인코딩 설정')
            self.dialog.setComboBoxItems(['자동', 'EUC-KR', 'UTF-8', 'UTF-16LE', 'UTF-16BE', '직접 입력'])
            self.dialog.show()

            self.dialog.textValueChanged.connect(type_encode)
            self.dialog.textValueSelected.connect(set_encode)

        def type_encode(text):
            if text == '직접 입력':
                self.dialog.setComboBoxItems([])
                self.dialog.setComboBoxEditable(True)

        def set_encode(text):
            self.encoding = text

        def save_dir():
            selected = QFileDialog.getExistingDirectory(self, "저장 위치 선택")
            self.save_dir_line.setText(selected)

        def apply():
            self.ok_button.setEnabled(False)
            ques = Utils.read_list(self.que_list, False)
            files = Utils.read_list(self.file_list, False)
            strip = False if self.save_strip.isChecked() else True
            log = []
            for file in files:
                try:
                    text = Utils.launch_que(file[1], ques, self.encoding, strip)
                    if len(text):
                        if self.save_orig_radio.isChecked():
                            savepath = file[1]
                        else:
                            savepath = self.save_dir_line.text() + '/' + file[0]
                        Utils.save_file(savepath, text)
                except Exception as e:
                    log.append(file[0] + ' 처리 오류 : ' + str(e))
            if log:
                ScrollMessageBox(QMessageBox.Warning, 'Batch SAMI Sync', "\n".join(log))

            else:
                QMessageBox.information(self, 'Batch SAMI Sync', '변환 완료!')
            self.ok_button.setEnabled(True)

        self.tab1_add_button.clicked.connect(tab1_add)
        self.tab2_add_button.clicked.connect(tab2_add)
        self.tab3_add_button.clicked.connect(tab3_add)
        self.que_delete_button.clicked.connect(lambda: Utils.delete_list(self.que_list))
        self.que_clear_button.clicked.connect(lambda: Utils.clear_list(self.que_list))
        self.que_up_button.clicked.connect(lambda: Utils.up_list(self.que_list))
        self.que_down_button.clicked.connect(lambda: Utils.down_list(self.que_list))
        self.file_file_open.clicked.connect(file_open)
        self.file_dir_open.clicked.connect(dir_open)
        self.file_delete.clicked.connect(lambda: Utils.delete_list(self.file_list))
        self.file_clear.clicked.connect(lambda: Utils.clear_list(self.file_list))
        self.file_encode.clicked.connect(open_encode_dialog)
        self.save_dir_find.clicked.connect(save_dir)
        self.ok_button.clicked.connect(apply)
        self.cancel_button.clicked.connect(sys.exit)

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls:
            event.accept()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        if event.mimeData().hasUrls():
            event.setDropAction(Qt.CopyAction)
            event.accept()
            for url in event.mimeData().urls():
                if url.isLocalFile():
                    file = str(url.toLocalFile())
                    if fnmatch(file, '*.smi'):
                        name = ntpath.basename(file)
                        Utils.insert_list(self.file_list, name, file)
                    elif not fnmatch(file, '*.*'):
                        for paths, subdirs, files in os.walk(file):
                            for file in files:
                                if fnmatch(file, '*.smi'):
                                    name = ntpath.basename(file)
                                    Utils.insert_list(self.file_list, name, file)

        else:
            event.ignore()