Beispiel #1
0
def check_auth(username, password):
    """This function is called to check if a username /
    password combination is valid.
    """
    global APP_RESOURCE
    config = ConfigHelper(APP_RESOURCE)
    auth_username = config.get_property_by_section('server', 'auth.username')
    auth_password = config.get_property_by_section('server', 'auth.password')
    return username == auth_username and password == auth_password
Beispiel #2
0
def config_db_url(resource):
    config = ConfigHelper(resource)
    url = 'postgresql+psycopg2://{user}:{pw}@{host}:{port}/{db}'.format(
        db=config.get_property_by_section('datasource', 'inova.db.datasource'),
        host=config.get_property_by_section('datasource', 'inova.db.host'),
        port=config.get_property_by_section('datasource', 'inova.db.port'),
        user=config.get_property_by_section('datasource', 'inova.db.username'),
        pw=config.get_property_by_section('datasource', 'inova.db.password'))
    return url
Beispiel #3
0
def initialize_config():
    path = os.path.dirname(os.path.realpath(__file__))
    fileConfig(f"{path}/config/log.ini")
    # fileConfig('./config/log.ini')
    config = ConfigHelper()

    return {
        'base_url': config.get('server', 'base_url'),
        'port': config.get('server', 'port'),
        'workers': config.get('server', 'workers'),
        'log_level': config.get('logging', 'level')
    }
Beispiel #4
0
    def __init__(self):

        config_helper = ConfigHelper()
        dialect = config_helper.get("database", "dialect")
        host = config_helper.get("database", "host")
        port = config_helper.get("database", "port")
        name = config_helper.get("database", "name")
        username = config_helper.get("database", "username")
        password = config_helper.get("database", "password")

        # TODO: I couldn't get MySQL to work without added pymysql to the connection string
        # SQLAlchemy uses mysqldb by default but I couldn't find that module in PyPi
        self._engine = create_engine(
            f"{dialect}+pymysql://{username}:{password}@{host}:{port}/{name}")
        self.session_factory = sessionmaker(bind=self._engine)
        self.session = scoped_session(self.session_factory)
    def __init__(self, parent=None):
        super(TraySetting, self).__init__(parent)
        self.setupUi(self)

        # 初始化数据
        self.__db_helper = DBHelper(self)
        self.__config_helper = ConfigHelper(self)
        self.__sql_where = self.__config_helper.get_config_key(
            self.__config_section_background, "sqlWhere")
        self.textEdit_sqlWhere.setText(self.__sql_where)
        self.__time_interval = int(
            self.__config_helper.get_config_key(
                self.__config_section_background, "timeIntervalInMin"))
        self.lineEdit_min.setText(str(self.__time_interval))

        self.__current_image = None

        self.pushButton_save.pressed.connect(self.__save)
        self.pushButton_cancel.pressed.connect(self.hide)

        # 系统托盘
        self.__tray = QtWidgets.QSystemTrayIcon()
        self.__tray.setIcon(QtGui.QIcon("images/tranIcon.png"))
        self.__tray.setToolTip("壁纸切换")
        self.__tray.activated[QSystemTrayIcon.ActivationReason].connect(
            self.__on_tray_click)
        menu = QtWidgets.QMenu()

        # 创建显示器对应壁纸项
        self._monitors = get_monitors()
        self.__levels = self.__db_helper.get_model_data_list('level')
        self.create_monitor_menu(menu)

        # 壁纸切换方式
        self.__change_type_actions = list()
        self.create_change_type_menu(menu)

        switch_next = QtWidgets.QAction("切换下一张", self)
        switch_next.triggered.connect(self.__change_background)
        menu.addAction(switch_next)
        # 加载默认参数
        type_value = self.__config_helper.get_config_key(
            self.__config_section_background, self.__config_key_change_type,
            ChangeType.Order.value)
        self.__change_type = ChangeType(int(type_value))
        self.__update_change_type_action(self.__change_type)
        offset = self.__config_helper.get_config_key(
            self.__config_section_background,
            self.__config_key_last_order_image_offset, 0)
        self.__last_order_image_offset = int(offset)

        menu.addSeparator()
        setting = QtWidgets.QAction("设置", self)
        setting.triggered.connect(self.show)
        menu.addAction(setting)
        close = QtWidgets.QAction("退出", self)
        close.triggered.connect(self.close)
        menu.addAction(close)
        self.__tray.setContextMenu(menu)
        self.__tray.show()

        threading.Thread(target=self.__change_windows_background_timely,
                         daemon=True).start()
class TraySetting(QtWidgets.QWidget, Ui_TraySetting):
    __config_section_background = "background"
    __config_key_change_type = "changeType"
    __config_key_last_order_image_offset = "lastOrderImageOffset"
    _monitor_start_y = 0
    _monitor_settings = []  # 显示器设定列表

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

        # 初始化数据
        self.__db_helper = DBHelper(self)
        self.__config_helper = ConfigHelper(self)
        self.__sql_where = self.__config_helper.get_config_key(
            self.__config_section_background, "sqlWhere")
        self.textEdit_sqlWhere.setText(self.__sql_where)
        self.__time_interval = int(
            self.__config_helper.get_config_key(
                self.__config_section_background, "timeIntervalInMin"))
        self.lineEdit_min.setText(str(self.__time_interval))

        self.__current_image = None

        self.pushButton_save.pressed.connect(self.__save)
        self.pushButton_cancel.pressed.connect(self.hide)

        # 系统托盘
        self.__tray = QtWidgets.QSystemTrayIcon()
        self.__tray.setIcon(QtGui.QIcon("images/tranIcon.png"))
        self.__tray.setToolTip("壁纸切换")
        self.__tray.activated[QSystemTrayIcon.ActivationReason].connect(
            self.__on_tray_click)
        menu = QtWidgets.QMenu()

        # 创建显示器对应壁纸项
        self._monitors = get_monitors()
        self.__levels = self.__db_helper.get_model_data_list('level')
        self.create_monitor_menu(menu)

        # 壁纸切换方式
        self.__change_type_actions = list()
        self.create_change_type_menu(menu)

        switch_next = QtWidgets.QAction("切换下一张", self)
        switch_next.triggered.connect(self.__change_background)
        menu.addAction(switch_next)
        # 加载默认参数
        type_value = self.__config_helper.get_config_key(
            self.__config_section_background, self.__config_key_change_type,
            ChangeType.Order.value)
        self.__change_type = ChangeType(int(type_value))
        self.__update_change_type_action(self.__change_type)
        offset = self.__config_helper.get_config_key(
            self.__config_section_background,
            self.__config_key_last_order_image_offset, 0)
        self.__last_order_image_offset = int(offset)

        menu.addSeparator()
        setting = QtWidgets.QAction("设置", self)
        setting.triggered.connect(self.show)
        menu.addAction(setting)
        close = QtWidgets.QAction("退出", self)
        close.triggered.connect(self.close)
        menu.addAction(close)
        self.__tray.setContextMenu(menu)
        self.__tray.show()

        threading.Thread(target=self.__change_windows_background_timely,
                         daemon=True).start()

    def create_monitor_menu(self, menu):
        """
        创建多显示器的菜单项
        :param menu:
        :return:
        """
        start_y = 0
        monitors = get_monitors()
        for i in range(len(monitors)):
            monitor = monitors[i]
            desc_action = QtWidgets.QAction("", self)
            desc_action.triggered.connect(
                partial(self.__open_file_in_directory_and_copy_file_name, i))
            menu.addAction(desc_action)

            if monitor.width > monitor.height:
                screen_state = '横屏'
            else:
                screen_state = '竖屏'
            level_menu = menu.addMenu(f" {monitor.name} {screen_state} - 等级")
            level_actions = []
            for level in self.__levels:
                level_action = level_menu.addAction(level.name)
                level_action.setCheckable(True)
                level_action.triggered.connect(
                    partial(self.__set_level, i, level.id))
                level_actions.append(level_action)
            menu.addSeparator()

            self._monitor_settings.append(
                MonitorSetting(monitor=monitor,
                               image_desc_action=desc_action,
                               image_level_actions=level_actions))
            start_y = min(start_y, monitor.y)
        self._monitor_start_y = start_y

    def create_change_type_menu(self, menu):
        change_type_menu = menu.addMenu("切换方式")
        change_order = self.__create_type_action("顺序", ChangeType.Order)
        change_type_menu.addAction(change_order)
        self.__change_type_actions.append(change_order)
        change_random = self.__create_type_action("随机", ChangeType.Random)
        change_type_menu.addAction(change_random)
        self.__change_type_actions.append(change_random)

    def __create_type_action(self, name, change_type):
        action = QtWidgets.QAction(name, self)
        action.setCheckable(True)
        action.triggered.connect(partial(self.__set_change_type, change_type))
        return action

    def __set_change_type(self, change_type: ChangeType):
        self.__change_type = change_type
        self.__update_change_type_action(change_type)

    def __update_change_type_action(self, change_type: ChangeType):
        if change_type == ChangeType.Order:
            self.__change_type_actions[0].setChecked(True)
            self.__change_type_actions[1].setChecked(False)
        elif change_type == ChangeType.Random:
            self.__change_type_actions[0].setChecked(False)
            self.__change_type_actions[1].setChecked(True)
        self.__config_helper.add_config_key(self.__config_section_background,
                                            self.__config_key_change_type,
                                            change_type.value)

    def __save(self):
        self.__sql_where = self.textEdit_sqlWhere.toPlainText()
        time_interval = self.lineEdit_min.text()
        self.__time_interval = int(time_interval)
        self.__config_helper.add_config_key("background", "sqlWhere",
                                            self.__sql_where)
        self.__config_helper.add_config_key("background", "timeIntervalInMin",
                                            time_interval)
        self.__last_order_image_offset = int(self.lineEdit_order_offset.text())
        self.__config_helper.add_config_key(
            self.__config_section_background,
            self.__config_key_last_order_image_offset,
            self.__last_order_image_offset)
        self.hide()

    def __change_windows_background_timely(self):
        while True:
            sleep_second = self.__time_interval * 60
            if self.__change_background():
                time.sleep(sleep_second)

    def __change_background(self):
        """
        修改桌面壁纸
        :return:
        """
        images = []
        start_y_list = []
        for i in range(len(self._monitor_settings)):
            setting = self._monitor_settings[i]
            image, index, count = self._get_image(
                setting.monitor.width >= setting.monitor.height)
            if image and os.path.exists(image.path):
                setting.image = image
            desc = f"[{index}/{count}] {image.author} - {image.filename}"
            if len(desc) > 50:
                desc = f"{desc[0:46]}..."
            setting.image_desc_action.setText(desc)
            self.__update_level_action(i, image.level_id)

            image_data = ImageHelper.get_sized_image(
                image.path,
                width=setting.monitor.width,
                height=setting.monitor.height)
            if image_data:
                images.append(image_data)
                start_y_list.append(setting.monitor.y - self._monitor_start_y)
        final_image_name = "final.jpg"
        ImageHelper.merge_horizontal_img(images, start_y_list,
                                         final_image_name)

        if len(images) != len(self._monitor_settings):
            QMessageBox.information(self, "提示", "sql 语句限制过多,获取不到图片",
                                    QMessageBox.Ok)
            return False

        path = os.path.join(os.getcwd(), final_image_name)
        key = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER,
                                    "Control Panel\\Desktop", 0,
                                    win32con.KEY_SET_VALUE)
        win32api.RegSetValueEx(key, "WallpaperStyle", 0, win32con.REG_SZ, "0")
        win32api.RegSetValueEx(key, "TileWallpaper", 0, win32con.REG_SZ, "1")
        win32gui.SystemParametersInfo(win32con.SPI_SETDESKWALLPAPER, path,
                                      1 + 2)
        return True

    def _get_image(self, is_horizontal):
        """
        从数据库获取下一张图片
        :param is_horizontal: 是否是横图
        :return: (图片信息,索引,总数)
        """
        sql_where = self.__sql_where
        if is_horizontal:
            operator = '>='
        else:
            operator = '<='
        sql_where += f' and width{operator}height'
        image_count = self.__db_helper.get_image_count(sql_where)
        if self.__change_type == ChangeType.Order:
            offset = self.__get_order_offset(image_count)
        else:
            offset = random.randint(0, image_count)
        image = self.__db_helper.get_one_image_with_where(sql_where, offset)
        print(
            f'where: {sql_where}, id: {image.id}, width: {image.width}, height: {image.height}, path: {image.path}'
        )
        return image, offset, image_count

    def __get_order_offset(self, image_count):
        """
        获取顺序切换时下一张图片的偏移量
        :param image_count: 图片总数
        :return: 偏移量
        """
        if self.__last_order_image_offset < image_count - 1:
            offset = self.__last_order_image_offset + 1
        else:
            offset = 0
        self.__last_order_image_offset = offset
        self.__config_helper.add_config_key(
            self.__config_section_background,
            self.__config_key_last_order_image_offset,
            self.__last_order_image_offset)
        if self.isHidden():
            self.lineEdit_order_offset.setText(str(offset))
        return offset

    def __update_level_action(self, index, level_id):
        """
        更新当前显示器壁纸等级状态
        :param index: 显示器索引
        :param level_id: 等级 id
        :return:
        """
        level_actions = self._monitor_settings[index].image_level_actions
        for i in range(len(self.__levels)):
            level = self.__levels[i]
            level_actions[i].setChecked(level.id == level_id)

    def __on_tray_click(self, reason: QSystemTrayIcon.ActivationReason):
        if reason == QSystemTrayIcon.Trigger:
            self.show()
            return

    def __open_file_in_directory_and_copy_file_name(self, index):
        image = self._monitor_settings[index].image
        file_path = image.path.replace('/', '\\')
        ex = f"explorer /select,{file_path}"
        os.system(ex)
        pyperclip.copy(image.filename)

    def __set_level(self, index, level_id):
        image = self._monitor_settings[index].image
        image.level_id = level_id
        self.__db_helper.update_image(image)
        self.__update_level_action(index, level_id)
#!/usr/bin/env python3
#
#   This script will run the main application file in debug mode.
#
#   author: Michael Gruber
#
#   Adapted by Lucas Nadalete

from sys import path
import os.path
path.append('src/main/python/')

from helper.config_helper import ConfigHelper
from webapp import application, APP_RESOURCE

# Load server configuration
helper = ConfigHelper(APP_RESOURCE)
chost = helper.get_property_by_section('server', 'inova.host')
cport = int(helper.get_property_by_section('server', 'inova.port'))
cdebug = bool(helper.get_property_by_section('server', 'inova.debug'))

# Start Web Server for all hosts, ssl_context=('cert.pem', 'key.pem')

if __name__ == "__main__":
    application.run(host=chost, debug=cdebug, port=cport)
Beispiel #8
0
        # '靠廢柴技能【狀態異常】.yaml',
        # '有點色公主殿下.yaml',
        # '和神獸在一起.yaml',
        # '創始魔法師.yaml',
        # '幸存煉金術師的城市慢活記.yaml',
        # '軍武宅轉開軍隊後宮.yaml'
        # '女兒已經升到了S級.yaml',

        # '田中~年齡等於單身資歷的魔法師~.yaml',
        # '世界最強後衛.yaml',
        # '鐵鏟無雙.yaml',
        '等級封頂的最強劍聖女碧翠斯也有弱點.yaml',
        # '因為不是真正的夥伴而被逐出勇者隊伍.yaml',
        # '勇者以想要成為朋友的視線看了過來!.yaml',
        # '孤單一人的異世界攻略.yaml',
        # '可以讀檔的旅店.yaml',
    ]
    for book_name in book_list:

        cwd = os.getcwd()
        config = ConfigHelper(config_name=book_name,
                              config_path=os.path.join(
                                  cwd, 'books_yaml')).get_config()
        # print(config)

        books = config['book']['books']
        book_info = config['book']['info']
        for book in books:
            print(book[0], book[1])
            main(book[0], book[1], book_info)
Beispiel #9
0
    def __init__(self, parent=None):
        super(ImageManager, self).__init__(parent)
        self.setupUi(self)

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

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

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

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

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

        self.listView.setModel(self.__image_model)

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

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

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

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

        # 预加载图片
        threading.Thread(target=self.__preload, daemon=True).start()
Beispiel #10
0
class ImageManager(QMainWindow, Ui_Manager):
    _signal_update_image_id = pyqtSignal(QModelIndex, int)

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

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

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

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

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

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

        self.listView.setModel(self.__image_model)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    # endregion

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

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

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

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

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

    # endregion

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

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

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

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