Esempio n. 1
1
class SideBarWidget(QFrame):
    """
    """
    def __init__(self, parent):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self.parent_widget = parent
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.MinimumExpanding)
        self._widget_layout = QHBoxLayout(self)

        # widget stuff
        self._d_widget = QWidget(self)
        self._d_widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.MinimumExpanding)
        self._widget_layout.addWidget(self._d_widget)
        self.main_layout = QVBoxLayout(self._d_widget)
        self.main_layout.setSpacing(0)
        self.main_layout.setContentsMargins(0,0,0,0)
        self.arrow_handle = misc.ArrowHandle(self)
        self.arrow_handle.CLICKED.connect(self.slide)

        self._widget_layout.addWidget(self.arrow_handle)
        self.setContentsMargins(0,0,-self.arrow_handle.width(),0)

        self.show_all_galleries_btn = QPushButton("Show all galleries")
        self.show_all_galleries_btn.clicked.connect(lambda:parent.manga_list_view.sort_model.set_gallery_list())
        self.show_all_galleries_btn.clicked.connect(self.show_all_galleries_btn.hide)
        self.show_all_galleries_btn.setIcon(app_constants.CROSS_ICON_WH)
        self.show_all_galleries_btn.hide()
        self.main_layout.addWidget(self.show_all_galleries_btn)
        self.main_buttons_layout = QHBoxLayout()
        self.main_layout.addLayout(self.main_buttons_layout)

        # buttons
        bgroup = QButtonGroup(self)
        bgroup.setExclusive(True)
        self.lists_btn = QPushButton("")
        self.lists_btn.setIcon(app_constants.G_LISTS_ICON_WH)
        self.lists_btn.setCheckable(True)
        bgroup.addButton(self.lists_btn)
        self.artist_btn = QPushButton("")
        self.artist_btn.setIcon(app_constants.ARTISTS_ICON)
        self.artist_btn.setCheckable(True)
        bgroup.addButton(self.artist_btn)
        self.ns_tags_btn = QPushButton("")
        self.ns_tags_btn.setIcon(app_constants.NSTAGS_ICON)
        self.ns_tags_btn.setCheckable(True)
        bgroup.addButton(self.ns_tags_btn)
        self.lists_btn.setChecked(True)


        self.main_buttons_layout.addWidget(self.lists_btn)
        self.main_buttons_layout.addWidget(self.artist_btn)
        self.main_buttons_layout.addWidget(self.ns_tags_btn)

        # buttons contents
        self.stacked_layout = QStackedLayout()
        self.main_layout.addLayout(self.stacked_layout)

        # lists
        gallery_lists_dummy = QWidget(self)
        self.lists = GalleryLists(self)
        create_new_list_btn = QPushButton()
        create_new_list_btn.setIcon(QIcon(app_constants.PLUS_ICON))
        create_new_list_btn.setIconSize(QSize(15, 15))
        create_new_list_btn.clicked.connect(lambda: self.lists.create_new_list())
        create_new_list_btn.adjustSize()
        create_new_list_btn.setFixedSize(create_new_list_btn.width(), create_new_list_btn.height())
        create_new_list_btn.setToolTip("Create a new list!")
        lists_l = QVBoxLayout(gallery_lists_dummy)
        lists_l.setContentsMargins(0,0,0,0)
        lists_l.setSpacing(0)
        lists_l.addWidget(self.lists)
        lists_l.addWidget(create_new_list_btn)
        lists_index = self.stacked_layout.addWidget(gallery_lists_dummy)
        self.lists.GALLERY_LIST_CLICKED.connect(parent.manga_list_view.sort_model.set_gallery_list)
        self.lists.GALLERY_LIST_CLICKED.connect(self.show_all_galleries_btn.show)
        self.lists.GALLERY_LIST_REMOVED.connect(self.show_all_galleries_btn.click)
        self.lists_btn.clicked.connect(lambda:self.stacked_layout.setCurrentIndex(lists_index))
        self.show_all_galleries_btn.clicked.connect(self.lists.clearSelection)
        self.show_all_galleries_btn.clicked.connect(self.lists._reset_selected)

        # artists
        self.artists_list = GalleryArtistsList(parent.manga_list_view.gallery_model, self)
        self.artists_list.artist_clicked.connect(lambda a: parent.search('artist:"{}"'.format(a)))
        artists_list_index = self.stacked_layout.addWidget(self.artists_list)
        self.artist_btn.clicked.connect(lambda:self.stacked_layout.setCurrentIndex(artists_list_index))
        #self.lists.GALLERY_LIST_CLICKED.connect(self.artists_list.set_current_glist)
        self.show_all_galleries_btn.clicked.connect(self.artists_list.clearSelection)
        #self.show_all_galleries_btn.clicked.connect(lambda:self.artists_list.set_current_glist())

        # ns_tags
        self.tags_tree = TagsTreeView(self)
        self.tags_tree.TAG_SEARCH.connect(parent.search)
        self.tags_tree.NEW_LIST.connect(self.lists.create_new_list)
        self.tags_tree.setHeaderHidden(True)
        self.show_all_galleries_btn.clicked.connect(self.tags_tree.clearSelection)
        self.tags_layout = QVBoxLayout(self.tags_tree)
        ns_tags_index = self.stacked_layout.addWidget(self.tags_tree)
        self.ns_tags_btn.clicked.connect(lambda:self.stacked_layout.setCurrentIndex(ns_tags_index))

        self.slide_animation = misc.create_animation(self, "maximumSize")
        self.slide_animation.stateChanged.connect(self._slide_hide)
        self.slide_animation.setEasingCurve(QEasingCurve.InOutQuad)

    def _slide_hide(self, state):
        size = self.sizeHint()
        if state == self.slide_animation.Stopped:
            if self.arrow_handle.current_arrow == self.arrow_handle.OUT:
                self._d_widget.hide()
        elif self.slide_animation.Running:
            if self.arrow_handle.current_arrow == self.arrow_handle.IN:
                if not self.parent_widget.current_manga_view.allow_sidebarwidget:
                    self.arrow_handle.current_arrow = self.arrow_handle.OUT
                    self.arrow_handle.update()
                else:
                    self._d_widget.show()


    def slide(self, state):
        self.slide_animation.setEndValue(QSize(self.arrow_handle.width() * 2, self.height()))

        if state:
            self.slide_animation.setDirection(self.slide_animation.Forward)
            self.slide_animation.start()
        else:
            self.slide_animation.setDirection(self.slide_animation.Backward)
            self.slide_animation.start()

    def showEvent(self, event):
        super().showEvent(event)
        if not app_constants.SHOW_SIDEBAR_WIDGET:
            self.arrow_handle.click()

    def _init_size(self, event=None):
        h = self.parent_widget.height()
        self._max_width = 250
        self.updateGeometry()
        self.setMaximumWidth(self._max_width)
        self.slide_animation.setStartValue(QSize(self._max_width, h))

    def resizeEvent(self, event):
        self._init_size(event)
        return super().resizeEvent(event)
class Window(QWidget):
    ''' Class Window '''
    def __init__(self):
        super().__init__()
        self.setWindowTitle('QStackedLayout Example')
        # Create a top leval layout
        layout = QVBoxLayout()
        self.setLayout(layout)
        # Create and connect a combo box to switch between pages
        self.pageCombo = QComboBox()
        self.pageCombo.addItems(['Page 1', 'Page 2'])
        self.pageCombo.activated.connect(self.switchPage)
        # Create stacked layout
        self.stackedLayout = QStackedLayout()
        # Create first page
        self.page1 = QWidget()
        self.page1Layout = QFormLayout()
        self.page1Layout.addRow('Name: ', QLineEdit())
        self.page1Layout.addRow('Address: ', QLineEdit())
        self.page1.setLayout(self.page1Layout)
        self.stackedLayout.addWidget(self.page1)
        # Create second page
        self.page2 = QWidget()
        self.page2Layout = QFormLayout()
        self.page2Layout.addRow('Job: ', QLineEdit())
        self.page2Layout.addRow('Department: ', QLineEdit())
        self.page2.setLayout(self.page2Layout)
        self.stackedLayout.addWidget(self.page2)
        # Add combo and stacked layout to top-level layout
        layout.addWidget(self.pageCombo)
        layout.addLayout(self.stackedLayout)

    def switchPage(self):
        ''' Define how sitch page '''
        self.stackedLayout.setCurrentIndex(self.pageCombo.currentIndex())
Esempio n. 3
0
class Demo4(QDialog):
    # stackl = QStackedLayout()
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self.resize(400, 600)
        mainl = QGridLayout(self)
        self.stackl = QStackedLayout()
        self.stackl.setStackingMode(QStackedLayout.StackAll)

        for i in range(10):
            lab = QLabel(self)
            lab.setText('%d_label' % (i + 1))
            lab.setMinimumSize(100, 100)

            lab.setAutoFillBackground(True)

            pt = lab.palette()
            # pt = QPalette()
            pt.setBrush(QPalette.Background, Qt.black)
            pt.setBrush(QPalette.WindowText, Qt.white)
            lab.setPalette(pt)
            lab.setAlignment(Qt.AlignCenter)
            self.stackl.addWidget(lab)
            btn = QPushButton('item:%d' % (i + 1))
            mainl.addWidget(btn, i, 0)
            btn.clicked.connect(self.on_btnClick)

        mainl.addLayout(self.stackl, 0, 1, self.stackl.count(), 1)

    def on_btnClick(self):
        btn = QPushButton()
        i = int(self.sender().text()[len('item:'):]) - 1
        print('btn:', i)
        self.stackl.setCurrentIndex(i)
Esempio n. 4
0
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        self.color_index = 3

        self.setWindowTitle("Jayone's Awesome App")

        layout = QVBoxLayout()
        layout2 = QStackedLayout()

        layout2.addWidget(Color('red'))
        layout2.addWidget(Color('green'))
        layout2.addWidget(Color('blue'))
        layout2.addWidget(Color('yellow'))

        layout2.setCurrentIndex(self.color_index)

        layout.addLayout(layout2)
        self.stack_layout = layout2

        push_button = QPushButton('change')
        push_button.clicked.connect(self.button_click)
        layout.addWidget(push_button)

        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
Esempio n. 5
0
class TextEditDemo(QWidget):
    def __init__(self, parent=None):
        super(TextEditDemo, self).__init__(parent)
        self.setWindowTitle("QStackedLayout 例子")
        self.resize(300, 270)
        # 创建堆叠布局

        self.btnPress1 = QPushButton("FormA")
        self.btnPress2 = QPushButton("FormB")

        self.form1 = FormA()
        self.form2 = FormB()

        widget = QWidget()
        self.stacked_layout = QStackedLayout()
        widget.setLayout(self.stacked_layout)
        widget.setStyleSheet("background-color:grey;")
        self.stacked_layout.addWidget(self.form1)
        self.stacked_layout.addWidget(self.form2)

        layout = QVBoxLayout()
        layout.addWidget(widget)
        layout.addWidget(self.btnPress1)
        layout.addWidget(self.btnPress2)

        self.setLayout(layout)
        self.btnPress1.clicked.connect(self.btnPress1_Clicked)
        self.btnPress2.clicked.connect(self.btnPress2_Clicked)

    def btnPress1_Clicked(self):
        self.stacked_layout.setCurrentIndex(0)

    def btnPress2_Clicked(self):
        self.stacked_layout.setCurrentIndex(1)
Esempio n. 6
0
class Duidie(QtWidgets.QWidget, Ui_Form):
    def __init__(self):
        super(Duidie, self).__init__()
        self.setupUi(self)
        self.pushButton.clicked.connect(self.show_panel)
        self.pushButton_2.clicked.connect(self.show_panel)
        #设置堆叠布局给self.frame
        self.qsl = QStackedLayout(self.frame_2)

        one = One_panel()
        two = Two_panel()

        self.qsl.addWidget(one)
        self.qsl.addWidget(two)

    def show_panel(self):
        try:
            dic = {
                "pushButton": 0,
                "pushButton_2": 1,
            }
            index = dic[self.sender().objectName()]  #获取当前点击按钮的名称,结合字典获得索引
            self.qsl.setCurrentIndex(index)  #通过索引设置堆叠布局展示的页面
        except Exception as e:
            print(str(e))
Esempio n. 7
0
class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QStackedLayout Example")
        # Create a top-level layout
        layout = QVBoxLayout()
        self.setLayout(layout)
        # Create and connect the combo box to switch between pages
        self.pageCombo = QComboBox()
        self.pageCombo.addItems(["Page 1", "Page 2"])
        self.pageCombo.activated.connect(self.switchPage)
        # Create the stacked layout
        self.stackedLayout = QStackedLayout()
        # Create the first page
        self.page1 = QWidget()
        self.page1Layout = QFormLayout()
        self.page1Layout.addRow("Name:", QLineEdit())
        self.page1Layout.addRow("Address:", QLineEdit())
        self.page1.setLayout(self.page1Layout)
        self.stackedLayout.addWidget(self.page1)
        # Create the second page
        self.page2 = QWidget()
        self.page2Layout = QFormLayout()
        self.page2Layout.addRow("Job:", QLineEdit())
        self.page2Layout.addRow("Department:", QLineEdit())
        self.page2.setLayout(self.page2Layout)
        self.stackedLayout.addWidget(self.page2)
        # Add the combo box and the stacked layout to the top-level layout
        layout.addWidget(self.pageCombo)
        layout.addLayout(self.stackedLayout)

    def switchPage(self):
        self.stackedLayout.setCurrentIndex(self.pageCombo.currentIndex())
Esempio n. 8
0
class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.directory = None
        self.paths = None

        self.setWindowTitle("DE-DUPE")
        self.resize(800, 480)

        self.layout = QStackedLayout()

        self.select_directory = SelectDirectoryWindow(
            self.on_directory_selected)
        self.start_dectection = StartDetectionWindow(
            self.on_find_duplicates_clicked)
        self.detection = DetectionWindow(self.on_image_infos_calculated)
        self.find_duplicates = FindDuplicatesWindow(self.on_duplicates_found)
        self.resolve_duplicates = ResolveDuplicatesWindow(
            self.on_duplicates_resolved)
        self.summary = SummaryWindow()

        self.layout.addWidget(self.select_directory)
        self.layout.addWidget(self.start_dectection)
        self.layout.addWidget(self.detection)
        self.layout.addWidget(self.find_duplicates)
        self.layout.addWidget(self.resolve_duplicates)
        self.layout.addWidget(self.summary)

        self.setLayout(self.layout)

    def on_directory_selected(self, directory):
        self.directory = directory
        self.paths = glob.glob(os.path.join(directory, "*.jpg"))

        self.start_dectection.set_paths(self.paths)
        self.layout.setCurrentIndex(1)

    def on_find_duplicates_clicked(self):
        self.detection.set_paths(self.paths)
        self.layout.setCurrentIndex(2)

    def on_image_infos_calculated(self, image_infos):
        self.image_infos = image_infos
        self.find_duplicates.set_image_infos(image_infos)
        self.layout.setCurrentIndex(3)

    def on_duplicates_found(self, duplicates):
        self.duplicates = duplicates
        self.resolve_duplicates.set_duplicates(duplicates, self.directory)
        self.layout.setCurrentIndex(4)

    def on_duplicates_resolved(self):
        self.duplicate_paths = glob.glob(
            os.path.join(self.directory, "duplicates", "*.jpg"))
        self.summary.set_summary(len(self.paths), len(self.duplicates),
                                 len(self.duplicate_paths))
        self.layout.setCurrentIndex(5)
Esempio n. 9
0
class ApexRes(WindowWidget):

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

        self.setWindowTitle("Apex Res")
        self.showMaximized()
        self.configureToolBar()

        self.central_widget = QWidget(self)
        self.central_widget.setMouseTracking(True)
        
        self.stacked_layout = QStackedLayout(self.central_widget)
        self.addChromatogramWidget()
        
        self.setCentralWidget(self.central_widget)    
        
    def configureToolBar(self):
        
        ribbon_toolbar = RibbonToolBar(self)
        
        home_menu = ribbon_toolbar.addMenu('Home')
        chrom_group = ribbon_toolbar.addGroup('\nChromatogram\n\n', home_menu)
        chrom_group.button.clicked.connect(self.addChromatogramWidget)
        data_group = ribbon_toolbar.addGroup('\nData\nEntry\n\n', home_menu)
        data_group.button.clicked.connect(self.addDataEntryWidget)
        self.slider_check = ribbon_toolbar.addSliderChoiceWidget(home_menu)

        mode_menu = ribbon_toolbar.addMenu('Mode')
        ig2_group = ribbon_toolbar.addGroup('Isocratic\nto\nGradient\n\n2 runs', mode_menu)
        gg2_group = ribbon_toolbar.addGroup('Gradient\nto\nGradient\n\n2 runs', mode_menu)
        ig3_group = ribbon_toolbar.addGroup('Isocratic\nto\nGradient\n\n3 runs', mode_menu)
        gg3_group = ribbon_toolbar.addGroup('Gradient\nto\nGradient\n\n3 runs', mode_menu)
        ph3_group = ribbon_toolbar.addGroup('\npH\n\n\n3 runs', mode_menu)
        t2_group = ribbon_toolbar.addGroup('\nTemperature\n\n\n2 runs', mode_menu)
        gt4_group = ribbon_toolbar.addGroup('Gradient\nand\nTemperature\n\n4 runs', mode_menu)
        it4_group = ribbon_toolbar.addGroup('Isocratic\nand\nTemperature\n\n4 runs', mode_menu)
        gph6_group = ribbon_toolbar.addGroup('Gradient\nand\npH\n\n6 runs', mode_menu)
        iph6_group = ribbon_toolbar.addGroup('Isocratic\nand\npH\n\n6 runs', mode_menu)

        ins_menu = ribbon_toolbar.addMenu('Instrument')
        edit_ins_group = ribbon_toolbar.addGroup('\nEdit\nInstrument\n\n', ins_menu)
        
        self.addToolBar(ribbon_toolbar)
                
    def addChromatogramWidget(self):
        chromatogram_widget = ChromatogramWidget()
        self.stacked_layout.addWidget(chromatogram_widget)
        self.stacked_layout.setCurrentIndex(0)
        if self.slider_check.clayout.count() == 0:
            chromatogram_widget.initialiseSliderCheckBoxes(self.slider_check)
        chromatogram_widget.initialiseSliders()

    def addDataEntryWidget(self):
        data_widget = TwoGradOptimizeTable()
        self.stacked_layout.addWidget(data_widget)
        self.stacked_layout.setCurrentIndex(1)
Esempio n. 10
0
class SettingDialog(QDialog):
    def __init__(self, mainWindow):
        super(SettingDialog, self).__init__()
        self.setWindowFlags(Qt.WindowCloseButtonHint)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.mainWindow = mainWindow
        self.setWindowTitle('Settings')
        position = self.mainWindow.getPopUpPosition(600, 510)
        self.setGeometry(position.x(), position.y(), 600, 510)
        self.setFixedHeight(510)
        self.setFixedWidth(600)
        verticalLayout = QVBoxLayout()
        layout = QHBoxLayout()
        layout.setContentsMargins(5, 5, 5, 5)
        self.settingList = QListWidget()
        layout.addWidget(self.settingList, 1)
        settingContent = QWidget(self)
        self.layout = QStackedLayout()
        settingContent.setLayout(self.layout)
        self.setUpPages()
        self.settingList.itemSelectionChanged.connect(self.switchSettingPage)

        horizontalButtonLayout = QHBoxLayout()
        cancelButton = QPushButton("Cancel")
        cancelButton.clicked.connect(self.close)
        saveButton = QPushButton("Save")
        saveButton.clicked.connect(self.saveAll)
        horizontalButtonLayout.addWidget(saveButton)
        horizontalButtonLayout.addWidget(cancelButton)

        verticalLayout.addLayout(layout)
        verticalLayout.addLayout(horizontalButtonLayout)
        layout.addWidget(settingContent, 9)
        self.setLayout(verticalLayout)
        self.exec()

    def setUpPages(self):
        self.settingList.addItem("Main")
        self.layout.addWidget(MainWidget(self))
        self.settingList.addItem("Colors")
        self.layout.addWidget(ColorsWidget(self))
        self.settingList.addItem("Macros")
        self.layout.addWidget(MacrosWidget(self))
        self.settingList.addItem("Notification")
        self.layout.addWidget(NotificationWidget(self))

    def switchSettingPage(self):
        self.layout.setCurrentIndex(self.settingList.currentRow())

    def saveAll(self):
        for i in range(self.layout.count() - 1, -1, -1):
            self.layout.widget(i).saveSetting()
        self.accept()
class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        scriptDir = os.path.dirname(os.path.realpath(__file__))
        self.setWindowIcon(
            QtGui.QIcon(scriptDir + os.path.sep + 'Resources/Logo2.ico'))
        self.setMinimumSize(QSize(400, 100))
        self.setWindowTitle('TRG-Duplicate Tracker')

        bar = self.menuBar()
        account_menu = bar.addMenu('Accounts')
        login_action = QAction('Login', self)
        login_action.setIcon(
            QtGui.QIcon(scriptDir + os.path.sep + 'Resources/ChangeUser.ico'))
        login_action.triggered.connect(self.logout)

        logout_action = QAction('Logout', self)
        logout_action.setIcon(
            QtGui.QIcon(scriptDir + os.path.sep + 'Resources/LogOut.ico'))
        logout_action.triggered.connect(self.logout)

        signup_action = QAction('New Account', self)
        signup_action.setIcon(
            QtGui.QIcon(scriptDir + os.path.sep + 'Resources/AddUser.ico'))
        signup_action.triggered.connect(self.addUser)

        account_menu.addAction(login_action)
        account_menu.addAction(logout_action)
        account_menu.addAction(signup_action)

        self.newAccountWidget = SignUpWidget()
        self.loginWidget = LoginWidget()
        self.layout = QStackedLayout()

        self.layout.addWidget(self.loginWidget)
        self.layout.addWidget(self.newAccountWidget)

        widget = QWidget()
        widget.setLayout(self.layout)
        self.setCentralWidget(widget)

    def addUser(self):
        self.newAccountWidget.nameEdit.clear()
        self.newAccountWidget.PswdEdit.clear()
        self.newAccountWidget.pswdConEdit.clear()
        self.layout.setCurrentIndex(1)
        self.newAccountWidget.layout.setCurrentIndex(0)

    def logout(self):
        self.loginWidget.enterUserName.clear()
        self.loginWidget.enterPSWD.clear()
        self.layout.setCurrentIndex(0)
        self.loginWidget.layout.setCurrentIndex(0)
class LoginWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        scriptDir = os.path.dirname(os.path.realpath(__file__))
        url = QUrl.fromLocalFile(scriptDir + os.path.sep +
                                 'Resources/Alarm.mp3')
        content = QMediaContent(url)
        self.player = QMediaPlayer()
        self.player.setMedia(content)
        self.blankWidget = QWidget()
        self.dupWidget = DuplicateTracker()
        self.layout = QStackedLayout()

        self.usrn = ''
        self.pswd = ''
        self.initUI()

    def initUI(self):
        self.okButton = QPushButton('OK')
        self.okButton.clicked.connect(self.loginMethod)
        self.enterUserName = QLineEdit()
        self.enterUserName.setPlaceholderText('*****@*****.**')
        self.enterPSWD = QLineEdit()
        self.enterPSWD.setPlaceholderText('*******')
        self.enterPSWD.setEchoMode(QLineEdit.Password)

        self.hbox = QVBoxLayout()
        self.hbox.addWidget(self.enterUserName)
        self.hbox.addWidget(self.enterPSWD)
        self.hbox.addWidget(self.okButton)
        self.hbox.addStretch(1)

        self.blankWidget.setLayout(self.hbox)
        self.layout.addWidget(self.blankWidget)
        self.layout.addWidget(self.dupWidget)
        self.setLayout(self.layout)

    def loginMethod(self):
        self.usrn = self.enterUserName.text()
        self.pswd = self.enterPSWD.text()
        userBase = pd.read_csv(r'Users.csv', index_col='User')
        if self.usrn in userBase.index:
            epswd = str(self.pswd).encode('UTF-8')
            basepswd = str(userBase.loc[self.usrn, 'hash']).encode('UTF-8')
            if bcrypt.checkpw(epswd, basepswd):
                print('Password Accepted')
                self.layout.setCurrentIndex(1)
            else:
                print('incorrect password')
        else:
            print('user not found')
            print(userBase)
Esempio n. 13
0
class tools(QWidget):
    tool_list = ['推荐', '历史', 'tip', '公告']

    def __init__(self):
        self.db = DBModel()
        super().__init__()
        self.init()

    def init(self):
        self.btn_recommend = QPushButton("推荐")
        self.btn_history = QPushButton("历史上的今天")
        self.btn_tip = QPushButton("小tip")
        self.btn_announce = QPushButton("公告")

        frame = QFrame(self)

        self.qsl = QStackedLayout(frame)

        toolbar = QHBoxLayout()
        layout = QVBoxLayout()

        toolbar.addWidget(self.btn_recommend)
        toolbar.addWidget(self.btn_history)
        toolbar.addWidget(self.btn_tip)
        toolbar.addWidget(self.btn_announce)

        self.loadPages()
        self.btn_event()

        layout.addLayout(toolbar)
        layout.addWidget(frame)

        self.setLayout(layout)

    def loadPages(self):
        self.history_page = history()
        self.recommend_page = recommend()

        self.qsl.addWidget(self.recommend_page)
        self.qsl.addWidget(self.history_page)

    def setdb(self, db):
        self.db = db
        self.history_page.set_db(db)

    def btn_event(self):
        self.btn_recommend.clicked.connect(lambda: self.to_page(0))
        self.btn_history.clicked.connect(lambda: self.to_page(1))

    def to_page(self, num):
        print('切换:', self.tool_list[num], '页')
        self.qsl.setCurrentIndex(num)
Esempio n. 14
0
class TAOutputTab(QWidget):
    def __init__(self, ctx):
        super(TAOutputTab, self).__init__()
        self.ctx = ctx

        stacked_widget = QWidget()
        self.stacked_layout = QStackedLayout()
        self.stacked_layout.addWidget(self.create_empty_widget())
        self.stacked_layout.addWidget(self.create_data_widget())
        stacked_widget.setLayout(self.stacked_layout)

        layout = QHBoxLayout()
        layout.setContentsMargins(5, 5, 5, 5)
        layout.addWidget(stacked_widget)
        self.setLayout(layout)

    def display_empty(self):
        self.stacked_layout.setCurrentIndex(0)

    def display_data(self):
        self.stacked_layout.setCurrentIndex(1)

    def create_empty_widget(self):
        label = QLabel("You first have to successfully run the allocation.")
        label.setAlignment(Qt.AlignCenter)

        return label

    def create_data_widget(self):
        self.tabs = QTabWidget()
        self.tabs.setTabPosition(QTabWidget.South)
        return self.tabs

    def not_empty(self):
        m_tab = self.tableWidget.rowCount()
        n_tab = self.tableWidget.columnCount()
        return any(
            self.tableWidget.item(i, j).text() for j in range(n_tab)
            for i in range(m_tab))

    def update_tables_from_data(self):
        for i in range(self.tabs.count()):
            self.tabs.removeTab(0)

        for i in range(len(self.ctx.app_data.results)):
            self.tabs.addTab(TAAllocationOutputTab(self.ctx, i),
                             "Allocation {}".format(i + 1))

        return
Esempio n. 15
0
class Clue(QWidget):
    def __init__(self):
        super().__init__()
        self.beginScreen()
        self.startUI()
        self.show()

    def beginScreen(self):
        self.stackedLayout = QStackedLayout()
        self.setWindowTitle('Clue')
        self.move(700, 200)
        self.setFixedWidth(1000)
        self.setFixedHeight(500)
        self.setStyleSheet("background:" + sts.color_primary)

    def startUI(self):
        self.start = QWidget()
        grid = QGridLayout()

        titleLabel = QLabel("¿Podras saber quien fue el asesino?")
        titleLabel.setAlignment(QtCore.Qt.AlignCenter)
        titleLabel.setStyleSheet(sts.style_title)

        buttonStart = QPushButton("Iniciar")
        buttonStart.setCursor(QCursor(QtCore.Qt.PointingHandCursor))
        buttonStart.setStyleSheet(sts.style_button_start)
        buttonStart.clicked.connect(self.startGame)

        grid.addWidget(titleLabel, 0, 0)
        grid.addWidget(buttonStart, 1, 0)
        self.start.setLayout(grid)

        self.stackedLayout.addWidget(self.start)

        self.game = StartGame(self.moveMainFunction)
        self.stackedLayout.addWidget(self.game)

        self.end = EndGame(self.moveMainFunction)
        self.stackedLayout.addWidget(self.end)

        self.setLayout(self.stackedLayout)

    def startGame(self):
        self.moveMainFunction(1)

    def moveMainFunction(self, index):
        if index == 0:
            editHistNumber()
        self.stackedLayout.setCurrentIndex(index)
Esempio n. 16
0
class TaskWidget(QWidget):

    taskModified = pyqtSignal(tasklib.Task)
    taskDeleted = pyqtSignal(tasklib.Task)
    taskCreated = pyqtSignal(tasklib.Task)

    def __init__(self, parent, task, new=False):
        QWidget.__init__(self, parent)
        self.task = task
        self.new = new
        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.layout = QStackedLayout(self)

        self.label = QLabel(self)
        self.label.setTextFormat(Qt.RichText)
        self.label.setOpenExternalLinks(True)
        self._update()
        self.label.setWordWrap(True)
        self.layout.addWidget(self.label)

        self.editor = TaskEditorLineEdit(self, parent.mfile)
        self.layout.addWidget(self.editor)
        self.setLayout(self.layout)
        self.layout.setCurrentIndex(0)
        self.editor.editingFinished.connect(self.editFinished)
        self.task.modified.connect(self._update)

    def sizeHint(self):
        return self.label.size()

    def edit(self):
        self.editor.setText(self.task.text)
        self.layout.setCurrentIndex(1)
        self.editor.setFocus()

    def editFinished(self):
        # qt bug, this method may be called several times for one edit
        self.layout.setCurrentIndex(0)
        text = self.editor.text()
        if not text:
            self.taskDeleted.emit(self.task)
        elif self.new:
            self.task.text = text
            self.new = False
            self.taskCreated.emit(self.task)
        elif text != self.task.text:
            self.task.text = text
            self.taskModified.emit(self)
        self.parent().setFocus()

    def _update(self):
        self.label.setText(self.task.toHtml())

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.editor.setText(self.task.text)
            self.layout.setCurrentIndex(0)
            self.parent().setFocus()
Esempio n. 17
0
    def __init__(self):
        super().__init__()
        self.setWindowTitle('My Awesome App')

        layout = QStackedLayout()

        layout.addWidget(Color('red'))
        layout.addWidget(Color('green'))
        layout.addWidget(Color('blue'))
        layout.addWidget(Color('yellow'))

        layout.setCurrentIndex(3)

        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
Esempio n. 18
0
    def initUI(self):
        # Comment
        stacklayout = QStackedLayout()

        stacklayout.addWidget(Color('red'))
        stacklayout.addWidget(Color('green'))
        stacklayout.addWidget(Color('blue'))
        stacklayout.addWidget(Color('yellow'))

        stacklayout.setCurrentIndex(3)

        widget = QWidget()
        widget.setLayout(stacklayout)

        self.setGeometry(0, 0, 1440, 1080)
        self.setWindowTitle('Stack Layout')
        self.show()
Esempio n. 19
0
    def __init__(self):
        super().__init__()

        self.setWindowTitle("My App")

        layout = QStackedLayout()

        layout.addWidget(Color("red"))
        layout.addWidget(Color("green"))
        layout.addWidget(Color("blue"))
        layout.addWidget(Color("yellow"))

        layout.setCurrentIndex(1)

        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
Esempio n. 20
0
class AppDemo(QWidget):
    ppt = PPTController()

    def __init__(self):
        app = QApplication(sys.argv)
        # 화면 전환용 widget 설정
        self.widget = QStackedLayout()

        # 레이아웃 생성
        self.introLayout = IntroLayout()
        self.guideLayout = GuideLayout()
        self.fileUploadLayout = FileUploadLayout(self)
        self.webcamLayout = WebcamLayout()

        # widget 추가
        self.widget.addWidget(self.introLayout)
        self.widget.addWidget(self.guideLayout)
        self.widget.addWidget(self.fileUploadLayout)
        self.widget.addWidget(self.webcamLayout)

        self.introLayout.nextButton.clicked.connect(lambda: self.nextPage())

        self.guideLayout.nextButton.clicked.connect(lambda: self.nextPage())
        self.guideLayout.backButton.clicked.connect(lambda: self.backPage())

        self.fileUploadLayout.backButton.clicked.connect(
            lambda: self.backPage())
        self.fileUploadLayout.camTestButton.clicked.connect(
            lambda: self.nextPage())

        self.webcamLayout.backButton.clicked.connect(lambda: self.backPage())

        app.exec_()

    def start(self):
        self.webcamLayout.start()

    def nextPage(self):
        # max page : 4
        if self.widget.currentIndex() < 3:
            self.widget.setCurrentIndex(self.widget.currentIndex() + 1)

    def backPage(self):
        if self.widget.currentIndex() > 0:
            self.widget.setCurrentIndex(self.widget.currentIndex() - 1)
Esempio n. 21
0
class ReliatyRadio(QMainWindow):
    layout = None

    back = pyqtSignal()

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

        # Generic UI Settings
        self.resize(1024, 768)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        self.setSizePolicy(sizePolicy)
        self.setContextMenuPolicy(QtCore.Qt.NoContextMenu)

        self.show_main_menu()

    def init_layout(self):
        self.layout = QStackedLayout()
        self.setCentralWidget(QWidget())
        self.centralWidget().setSizePolicy(self.sizePolicy())
        self.centralWidget().resize(1024, 768)
        self.centralWidget().setLayout(self.layout)

    def add_and_show_widget(self, widget):
        self.layout.addWidget(widget)
        self.layout.setCurrentWidget(widget)

    def go_back(self):
        count = self.layout.count()
        self.layout.setCurrentIndex(count - 2)
        self.layout.takeAt(count - 1)

    def show_main_menu(self):
        self.clearLayout()
        self.add_and_show_widget(MainMenu())

    def clearLayout(self):
        while self.layout.count():
            child = self.layout.takeAt(0)
            if child.widget():
                child.widget().deleteLater()
Esempio n. 22
0
class history(QWidget):
    def __init__(self):
        self.db = DBModel()
        self.db = None
        super().__init__()
        self.init()

    def init(self):
        self.btn_search = QPushButton("查询记录")
        self.btn_addnew = QPushButton("新建")
        self.btn_search.setFixedSize(100, 40)
        self.btn_addnew.setFixedSize(100, 40)

        frame = QFrame()
        self.qsl = QStackedLayout(frame)

        self.search_page = history_search.history_search()
        self.add_page = history_add.history_add()
        self.qsl.addWidget(self.search_page)
        self.qsl.addWidget(self.add_page)

        leftside = QVBoxLayout()
        leftside.addWidget(self.btn_search)
        leftside.addWidget(self.btn_addnew)

        layout = QHBoxLayout()
        layout.addLayout(leftside)
        layout.addWidget(frame)

        self.click_event()

        self.setLayout(layout)

    def click_event(self):
        self.btn_search.clicked.connect(lambda: self.to_page(0))
        self.btn_addnew.clicked.connect(lambda: self.to_page(1))

    def set_db(self, db):
        self.db = db
        self.search_page.set_db(db)
        self.add_page.set_db(db)

    def to_page(self, num):
        self.qsl.setCurrentIndex(num)
Esempio n. 23
0
class MainWindow(QWidget):
    """
    Main window controller
    Attributes:
        videoWidget     Custom widget of type VideoWidget that represents the face recognition view.
        dialogWidget    Custom widget of type DialogWidget that represents the dialog view.
        stackLayout     Qt Layout that contains videoWidget and dialogWidget.
    """
    def __init__(self, videoWidget, dialogWidget):
        super().__init__()
        self.videoWidget = videoWidget
        self.dialogWidget = dialogWidget
        self.init_ui()

    def init_ui(self):
        """Method to initialize the UI: layouts and components"""
        self.setWindowTitle("Micchinetta")

        self.stackLayout = QStackedLayout()

        self.stackLayout.addWidget(self.videoWidget)
        self.stackLayout.addWidget(self.dialogWidget)

        self.setLayout(self.stackLayout)
        self.setMinimumSize(800, 600)
        self.show()
        #self.videoWidget.set_model(np.zeros((100,100, 3)))

    def resizeEvent(self, ev):
        """Slot for window resize event (Override)"""
        self.videoWidget.updateView()
        super().resizeEvent(ev)

    def activate_video(self):
        """Method to activate the videoWidget and face recognition thread"""
        self.dialogWidget.deactivate()
        self.videoWidget.activate()
        self.stackLayout.setCurrentIndex(0)

    def activate_dialog(self):
        """Method to activate the dialogWidget and dialog thread"""
        self.videoWidget.deactivate()
        self.dialogWidget.activate(self.videoWidget.face_recogniser.currentUser)
        self.stackLayout.setCurrentIndex(1)
Esempio n. 24
0
class Window(BaseWindow):
    def __init__(self):
        super(Window, self).__init__()

        self.last_index = 0

        self.setFixedSize(WINDOW_WIDTH, WINDOW_HEIGHT)
        # 要想使 move_center 起作用,必须设置 resize
        self.resize(WINDOW_WIDTH, WINDOW_HEIGHT)
        self.move_center()
        self.setWindowIcon(QIcon(abs_path('static/images/icon/logo.png')))
        self.setWindowTitle('江科大课表导出 v1.0')

        self.stack_layout = QStackedLayout()
        self.stack_layout.addWidget(StepHello(self.stack_layout))
        self.stack_layout.addWidget(StepAccount(self.stack_layout))
        self.stack_layout.addWidget(StepDate(self.stack_layout))
        self.stack_layout.addWidget(StepSuccess(self.stack_layout))
        self.stack_layout.addWidget(StepMistake(self.stack_layout))
        self.stack_layout.setCurrentIndex(self.last_index)
        self.stack_layout.currentChanged.connect(self.interface_changed)

        self.setLayout(self.stack_layout)
        self.show()

    def interface_changed(self):
        if self.last_index < self.stack_layout.currentIndex():
            self.last_index = self.stack_layout.currentIndex()
        elif self.last_index == 4 and self.stack_layout.currentIndex() == 0:
            for i in range(3)[::-1]:
                self.stack_layout.widget(i).step_ani(True)
            self.last_index = 0
        elif self.last_index in [3, 4
                                 ] and self.stack_layout.currentIndex() == 2:
            self.stack_layout.widget(2).step_ani(True)
            self.last_index = 2
        elif self.last_index == 2 and self.stack_layout.currentIndex() == 1:
            self.stack_layout.widget(1).step_ani(True)
            self.last_index = 1
        elif self.last_index == 1 and self.stack_layout.currentIndex() == 0:
            self.stack_layout.widget(0).step_ani(True)
            self.last_index = 0
Esempio n. 25
0
class GameTab(QWidget):
    def __init__(self, core):
        super(GameTab, self).__init__()
        self.layout = QStackedLayout()
        self.default_widget = Games(core)
        self.default_widget.game_list.show_game_info.connect(self.show_info)
        self.default_widget.head_bar.import_game.clicked.connect(lambda: self.layout.setCurrentIndex(2))
        self.layout.addWidget(self.default_widget)
        self.game_info = InfoTabs(core)
        self.game_info.info.update_list.connect(self.update_list)
        self.layout.addWidget(self.game_info)

        self.default_widget.head_bar.refresh_list.clicked.connect(self.update_list)

        self.import_widget = ImportWidget(core)
        self.layout.addWidget(self.import_widget)
        self.import_widget.back_button.clicked.connect(lambda: self.layout.setCurrentIndex(0))
        self.import_widget.update_list.connect(self.update_list)
        self.setLayout(self.layout)

    def update_list(self):
        self.default_widget.game_list.update_list(not self.default_widget.head_bar.view.isChecked())
        self.layout.setCurrentIndex(0)

    def show_info(self, app_name):
        self.game_info.update_game(app_name)
        self.game_info.setCurrentIndex(1)
        self.layout.setCurrentIndex(1)
Esempio n. 26
0
class FilterEntry(QWidget):
    # a widget that lets you switch between filter entries
    filterEntered = pyqtSignal(list)

    def __init__(self, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)
        self.current_entry = 0
        self.max_entries = 2
        self.text_entry = TextFilterEntry()
        dropdown_entry = DropdownFilterEntry()

        self.text_entry.filterEntered.connect(self.filterEntered)
        dropdown_entry.filterEntered.connect(self.filterEntered)

        self.entry_layout = QStackedLayout()
        self.entry_layout.addWidget(dropdown_entry)
        self.entry_layout.addWidget(self.text_entry)

        swap_button = QToolButton()
        swap_button.setText(">")
        swap_button.setToolTip("Switch between dropdown and text entry")
        swap_button.clicked.connect(self.next_entry)

        hlayout = QHBoxLayout()
        hlayout.addWidget(swap_button)
        hlayout.addLayout(self.entry_layout)
        self.setLayout(hlayout)
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().setSpacing(0)

    @pyqtSlot()
    def next_entry(self):
        self.current_entry += 1
        self.current_entry = self.current_entry % self.max_entries
        self.entry_layout.setCurrentIndex(self.current_entry)

    def set_entry(self, entry):
        self.current_entry = entry
        self.current_entry = self.current_entry % self.max_entries
        self.entry_layout.setCurrentIndex(self.current_entry)
Esempio n. 27
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("QStackedLayout + Box DEMO")

        page_layout = QVBoxLayout()
        button_layout = QHBoxLayout()
        self.stackLayout = QStackedLayout()

        page_layout.addLayout(
            button_layout)  # The page's button would be a vertical layout
        page_layout.addLayout(self.stackLayout)

        btn = QPushButton("red")
        btn.pressed.connect(self.tab_1_active)
        button_layout.addWidget(btn)

        btn = QPushButton("green")
        btn.pressed.connect(self.tab_2_active)
        button_layout.addWidget(btn)

        btn = QPushButton("blue")
        btn.pressed.connect(self.tab_3_active)
        button_layout.addWidget(btn)

        btn = QPushButton("purple")
        btn.pressed.connect(self.tab_4_active)
        button_layout.addWidget(btn)

        widget = QWidget()
        widget.setLayout(page_layout)
        self.setCentralWidget(widget)

        self.stackLayout.addWidget(Color("red"))
        self.stackLayout.addWidget(Color("green"))
        self.stackLayout.addWidget(Color("blue"))
        self.stackLayout.addWidget(Color("purple"))

    def tab_1_active(self):
        self.stackLayout.setCurrentIndex(0)

    def tab_2_active(self):
        self.stackLayout.setCurrentIndex(1)

    def tab_3_active(self):
        self.stackLayout.setCurrentIndex(2)

    def tab_4_active(self):
        self.stackLayout.setCurrentIndex(3)
Esempio n. 28
0
class MainWindow(StyledMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__(Strs.Main_Window_Title)
        # initialize all views
        self.__init_views()
        # resize to a proper one
        self.resize(*dim.main_window_size)

    def __init_views(self):
        # the base h-box
        self.__hbox_all = StyledHBox(spacing=10)
        # the menu list
        self.__lis_menu = MenuListWidget(
            VocAddingPage(0, Strs.Menu_List_Item_Add_Voc),
            PreferencesPage(1, Strs.Menu_List_Item_Preferences))
        self.__hbox_all.addWidget(self.__lis_menu, 0)
        # the right-side v-box
        self.__vbox_right = StyledVBox(spacing=10)
        self.__hbox_all.addLayout(self.__vbox_right, 1)
        # the stacked-layout for containing various pages
        self.__page_layout = QStackedLayout()
        for menu_widget in self.__lis_menu.get_all_widgets():
            self.__page_layout.addWidget(menu_widget.page.wrap_as_frame())
        self.__vbox_right.addLayout(self.__page_layout, 1)
        # the label for showing the source of used icons
        self.__lbl_icons_attribution = StyledLabel(
            cfg.icons_attribution_html_code, fixed_size=FixedSizes.TINY)
        self.__vbox_right.addWidget(self.__lbl_icons_attribution, 0,
                                    Qt.AlignBottom | Qt.AlignRight)
        # set the h-box as the layout of this window
        self.__frame_all = QFrame()
        self.__frame_all.setLayout(self.__hbox_all)
        self.setCentralWidget(self.__frame_all)
        # initially set to the first page
        self.set_page_by_index(0)

    # set the page
    def set_page_by_index(self, index: int):
        self.__page_layout.setCurrentIndex(index)
        self.__page_layout.currentWidget().layout().set_focus()
Esempio n. 29
0
class MainWindow(QWidget):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        self.actionAuswahl = QComboBox()
        self.actionAuswahl.addItem("Einen Sportler bearbeiten")
        self.actionAuswahl.addItem("Liste aller Sportler anzeigen")
        self.actionAuswahl.addItem("Eine Ergebnissliste bearbeiten")
        self.actionAuswahl.addItem("Ergebnisse & Urkunden drucken")
        self.actionAuswahl.addItem("Ergebnissvorlagen drucken")
        self.actionAuswahl.setEditable(False)
        self.actionAuswahl.activated.connect(self.setAction)

        self.actionSportlerEdit = SportlerEdit()
        self.actionSportlerShow = SportlerShow()
        self.actionErgebnissListe = ErgebnissListe()
        self.actionErgebnisseDrucken = ErgebnisseDrucken()
        self.actionErgebnissVorlage = ErgebnissVorlage()

        self.actionLayout = QStackedLayout()
        self.actionLayout.addWidget(self.actionSportlerEdit)
        self.actionLayout.addWidget(self.actionSportlerShow)
        self.actionLayout.addWidget(self.actionErgebnissListe)
        self.actionLayout.addWidget(self.actionErgebnisseDrucken)
        self.actionLayout.addWidget(self.actionErgebnissVorlage)


        self.mainLayout = QVBoxLayout()
        self.mainLayout.addWidget(self.actionAuswahl)
        self.mainLayout.addStretch(0)
        self.mainLayout.addLayout(self.actionLayout)
        self.mainLayout.addStretch(0)

        self.setLayout(self.mainLayout)
        self.show()

    def setAction(self,index):
        self.actionLayout.setCurrentIndex(index)
        self.actionLayout.currentWidget().update()
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle('My App')

        pagelayout = QVBoxLayout()
        button_layout = QHBoxLayout()
        self.stacklayout = QStackedLayout()

        pagelayout.addLayout(button_layout)
        pagelayout.addLayout(self.stacklayout)

        btn = QPushButton('red')
        btn.pressed.connect(self.activation_tab_0)
        button_layout.addWidget(btn)
        self.stacklayout.addWidget(Color('red'))

        btn = QPushButton('green')
        btn.pressed.connect(self.activation_tab_1)
        button_layout.addWidget(btn)
        self.stacklayout.addWidget(Color('green'))

        btn = QPushButton('blue')
        btn.pressed.connect(self.activation_tab_2)
        button_layout.addWidget(btn)
        self.stacklayout.addWidget(Color('blue'))

        btn = QPushButton('yellow')
        btn.pressed.connect(self.activation_tab_3)
        button_layout.addWidget(btn)
        self.stacklayout.addWidget(Color('yellow'))

        widget = QWidget()
        widget.setLayout(pagelayout)
        self.setCentralWidget(widget)

    def activation_tab_0(self):
        self.stacklayout.setCurrentIndex(0)

    def activation_tab_1(self):
        self.stacklayout.setCurrentIndex(1)

    def activation_tab_2(self):
        self.stacklayout.setCurrentIndex(2)

    def activation_tab_3(self):
        self.stacklayout.setCurrentIndex(3)
Esempio n. 31
0
class MyMainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MyMainWindow, self).__init__()
        self.setupUi(self)

        self.qsl = QStackedLayout(self.frame_2)
        dashboard_panel = DashboardPanel()
        data_source_panel = DataSourcePanel()
        schema_discovery_panel = SchemaDiscoveryPanel()
        schema_matching_panel = SchemaMatchingPanel()
        schema_summary_panel = SchemaSummaryPanel()
        queries_panel = QueriesPanel()
        data_quality_panel = DashboardPanel()
        about_panel = AboutPanel()

        self.qsl.addWidget(dashboard_panel)
        self.qsl.addWidget(data_source_panel)
        self.qsl.addWidget(schema_discovery_panel)
        self.qsl.addWidget(schema_matching_panel)
        self.qsl.addWidget(schema_summary_panel)
        self.qsl.addWidget(queries_panel)
        self.qsl.addWidget(data_quality_panel)
        self.qsl.addWidget(about_panel)

    def show_panel(self):
        dic = {
            'dashboard': 0,
            'data_source': 1,
            'schema_discovery': 2,
            'schema_matching': 3,
            'schema_summary': 4,
            'queries': 5,
            'data_quality': 6,
            'about': 7
        }
        index = dic[self.sender().objectName()]
        self.qsl.setCurrentIndex(index)
Esempio n. 32
0
class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        scriptDir = os.path.dirname(os.path.realpath(__file__))
        self.setWindowIcon(
            QtGui.QIcon(scriptDir + os.path.sep + 'Resources/Logo2.ico'))
        self.setMinimumSize(QSize(700, 200))
        self.setWindowTitle('TRG-Duplicate Tracker')

        bar = self.menuBar()
        Functions_menu = bar.addMenu('Functions')

        scan_action = QAction('Scan', self)
        scan_action.setIcon(
            QtGui.QIcon(scriptDir + os.path.sep + 'Resources/Scan.ico'))
        scan_action.triggered.connect(self.scanScreen)

        search_action = QAction('Search', self)
        search_action.setIcon(
            QtGui.QIcon(scriptDir + os.path.sep + 'Resources/Search.ico'))
        search_action.triggered.connect(self.searchScreen)

        CheckLoc_action = QAction('Check Locations', self)
        CheckLoc_action.setIcon(
            QtGui.QIcon(scriptDir + os.path.sep + 'Resources/Racks.ico'))
        CheckLoc_action.triggered.connect(self.rackScreen)

        Functions_menu.addAction(scan_action)
        Functions_menu.addAction(search_action)
        Functions_menu.addAction(CheckLoc_action)

        blankWidget = QWidget()
        self.dupWidget = DuplicateTracker()
        self.searchWidget = Searcher()
        self.rackwidget = Racker()

        self.layout = QStackedLayout()
        self.layout.addWidget(self.dupWidget)
        self.layout.addWidget(self.searchWidget)
        self.layout.addWidget(self.rackwidget)

        blankWidget.setLayout(self.layout)
        self.setCentralWidget(blankWidget)

    def searchScreen(self):
        self.layout.setCurrentIndex(1)

    def scanScreen(self):
        self.layout.setCurrentIndex(0)

    def rackScreen(self):
        self.layout.setCurrentIndex(2)
Esempio n. 33
0
class MainWindow(QWidget):
    """
    Root widget for application.
    Handles signals.
    """

    def __init__(self, items):
        """
        Init gui.
        Connect signals.
        Create layout.
        """

        super().__init__()

        self.items = items
        self.user = None
        self.communication = Communication()
        self.frames_layout = QStackedLayout()
        self.data_frame_index = None
        self.top_system_frame_height = 0

        self._init_gui()
        self._set_shortcuts()

    def _init_gui(self):
        self._set_sys_attributes()
        self._create_layout()
        self.communication.menu_btn_clicked.connect(self.menu_btn_clicked)
        self.show()
        self._first_start_check()

    def _first_start_check(self):

        def _import_templates(value):
            if value:
                from app.model import template
                template.Template.import_(options.INIT_TEMPLATES_PATH)
                self.show_message('Готово!')
            db.KeyValue(key=options.FIRST_START_KEY, value='').save()

        try:
            first_time = db.KeyValue.get(key=options.FIRST_START_KEY).value
        except (exc.NoResultFound, AttributeError):
            first_time = True

        if first_time:
            self.create_alert(options.FIRST_START_WELCOME_TEXT, _import_templates)

    def _create_layout(self):
        """
        Add TopFrame and main frames.
        """

        MessageWidget(self)
        vbox = QVBoxLayout()
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)
        self.setLayout(vbox)

        ActionButton(self)
        vbox.addWidget(TopFrame(self, self.items), stretch=15)
        vbox.addLayout(self.frames_layout, stretch=40)
        self._add_frames()
        self.show()

    def _add_frames(self):
        """
        Add frames to stacked layout.
        """

        frames = [
            DataWidget(self, self.items),
            TemplateWidget(self, self.items),
            DBWidget(self),
            OptionsWidget(self, self.items),
            UsersWidget(self),
        ]

        for i, frame in enumerate(frames):
            if isinstance(frame, DataWidget):
                self.data_frame_index = i
            if isinstance(frame, UsersWidget):
                self.user_frame_index = i
            self.frames_layout.addWidget(frame)

        self.frames_layout.setCurrentIndex(len(frames) - 1)

    def _set_sys_attributes(self):
        """
        Set sys attributes like window titile.
        Disable OS-specific buttons.
        Remove borders.
        """

        self.setWindowFlags(Qt.FramelessWindowHint)

        self.setWindowTitle('Hospital Helper')
        dw = QDesktopWidget()
        w = min(1300, dw.geometry().size().width() * 0.75)
        self.setFixedSize(w, w * 0.6)
        qr = self.frameGeometry()
        cp = dw.availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def menu_btn_clicked(self, index):
        """
        Callback to switch between main frames.
        """

        if self.frames_layout.currentIndex() == index == self.data_frame_index:
            self.communication.toggle_select_item.emit()
            return
        else:
            self.communication.set_select_item_visibility.emit(False)
        self.frames_layout.setCurrentIndex(index)

    def input_changed(self, item):
        """
        Change TopFrame label when client name is changed.
        Emit signal for TopFrame.
        """

        # Well it doesnt look good, but i was never good with UI.
        if self.items.index(item) == 0:
            text = []
            for i, key in enumerate(item.keys()):
                if item[key]:
                    text.append(str(item[key]))
                if i == 2:
                    break

            self.communication.input_changed_signal.emit(' '.join(text))

    def _set_shortcuts(self):
        """
        Set shortcuts for fast items switch on DataWidget.
        """

        def _shortcut_callback(key):
            if self.frames_layout.currentIndex() != self.data_frame_index:
                return
            self.communication.shortcut_pressed.emit(key)

        QShortcut(QKeySequence('Esc'), self).activated.connect(self.close)
        keys = [str(i) for i in range(0, 11)] + [chr(c) for c in range(ord('A'), ord('Z') + 1)]
        for key in keys:
            QShortcut(QKeySequence('Ctrl+{}'.format(key)), self).activated.connect(
                functools.partial(_shortcut_callback, key))

    def create_crud_widget(self, model, callback, db_object=None):
        """
        Add CrudWidget with self as parent
        """

        CrudWidget(self, model, callback, db_object)

    def create_alert(self, text, callback=None):
        AlertWidget(self, text, callback)

    def show_message(self, text):
        self.communication.set_message_text.emit(text)

    def user_selected(self, user, go_to_data_frame=False):
        """
        Callback when user is selected.
        Sets user, emits signal.
        """

        self.user = user
        self.communication.user_selected.emit(user)
        if go_to_data_frame:
            self.communication.menu_btn_clicked.emit(self.data_frame_index)

    def create_report(self):
        """
        Render and save report.
        Open report in default OS program.
        """

        r = report.Report(self.user, self.items)
        db_report = r.render_and_save()
        r.open(db_report.path)
        self.show_message('Отчет создан')

    def clean_input(self):
        def _clean_input(value):
            if not value:
                return

            self.communication.clean_items.emit()
            self.communication.input_changed_signal.emit('')
            for item in self.items:
                item.clean()
            self.show_message('Ok')

        self.create_alert('Очистить все поля?', _clean_input)

    def resized(self, top_frame, top_sys_btns, event):
        """
        Called when window is resized.
        Calculates Y position of the border between TopFrame and DataFrame
        """

        waterline = top_frame.y() + top_frame.height()
        self.top_system_frame_height = top_sys_btns.height()
        self.communication.resized.emit(self.width(), waterline, self.top_system_frame_height)

    def keyPressEvent(self, event):
        """
        If key is Ctrl - toggle SelectItemMenu visibility.
        If key is Ctrl+Return - opens ReportFrame
        Emits signal.
        """

        mods = event.modifiers()
        if mods & Qt.ControlModifier and self.frames_layout.currentIndex() == self.data_frame_index:
            if event.text() is '':
                self.communication.ctrl_hotkey.emit(True)
                return
            elif event.key() == Qt.Key_Return:
                self.communication.menu_btn_clicked.emit(self.data_frame_index + 1)

    def keyReleaseEvent(self, event):
        """
        If key is Ctrl - toggle SelectItemMenu visibility.
        """

        if event.text() is '':
            self.communication.ctrl_hotkey.emit(False)

    def close(self, event=None):
        """
        Close the application
        """

        QCoreApplication.instance().quit()

    def minimize(self, event=None):
        """
        Minimize the application
        """

        self.setWindowState(Qt.WindowMinimized)
Esempio n. 34
0
class AppWindow(QMainWindow):
	"The application's main window"
	def __init__(self):
		super().__init__()
		self.center = QWidget()
		self.display = QStackedLayout()
		self.center.setLayout(self.display)
		# init the manga view variables
		self.manga_display()
		# init the chapter view variables
		self.chapter_display()
		# init toolbar
		self.init_toolbar()
		# init status bar
		self.init_stat_bar()

		self.display.addWidget(self.manga_main)
		self.display.addWidget(self.chapter_main)

		self.setCentralWidget(self.center)
		self.setWindowTitle("Happypanda")
		self.resize(1029, 650)
		self.show()
	
	def init_stat_bar(self):
		self.status_bar = self.statusBar()
		self.status_bar.setMaximumHeight(20)
		self.status_bar.setSizeGripEnabled(False)
		self.stat_info = QLabel()
		self.stat_info.setIndent(5)
		self.sort_main = QAction("Asc", self)
		sort_menu = QMenu()
		self.sort_main.setMenu(sort_menu)
		s_by_title = QAction("Title", sort_menu)
		s_by_artist = QAction("Artist", sort_menu)
		sort_menu.addAction(s_by_title)
		sort_menu.addAction(s_by_artist)
		self.status_bar.addPermanentWidget(self.stat_info)
		#self.status_bar.addAction(self.sort_main)
		self.temp_msg = QLabel()
		self.temp_timer = QTimer()
		self.manga_list_view.series_model.ROWCOUNT_CHANGE.connect(self.stat_row_info)
		self.manga_list_view.series_model.STATUSBAR_MSG.connect(self.stat_temp_msg)

	def stat_temp_msg(self, msg):
		self.temp_timer.stop()
		self.temp_msg.setText(msg)
		self.status_bar.addWidget(self.temp_msg)
		self.temp_timer.timeout.connect(self.temp_msg.clear)
		self.temp_timer.setSingleShot(True)
		self.temp_timer.start(5000)

	def stat_row_info(self):
		r = self.manga_list_view.series_model.rowCount()
		t = len(self.manga_list_view.series_model._data)
		self.stat_info.setText("<b>Showing {} of {} </b>".format(r, t))

	def manga_display(self):
		"initiates the manga view"
		self.manga_main = QWidget()
		self.manga_main.setContentsMargins(-10, -12, -10, -10)
		self.manga_view = QHBoxLayout()
		self.manga_main.setLayout(self.manga_view)

		manga_delegate = series.CustomDelegate()
		manga_delegate.BUTTON_CLICKED.connect(self.setCurrentIndex)
		self.manga_list_view = series.MangaView()
		self.manga_list_view.setItemDelegate(manga_delegate)
		self.manga_view.addWidget(self.manga_list_view)

	def favourite_display(self):
		"initiates favourite display"
		pass

	def chapter_display(self):
		"Initiates chapter view"
		self.chapter_main = QWidget()
		self.chapter_main.setObjectName("chapter_main") # to allow styling this object
		self.chapter_layout = QHBoxLayout()
		self.chapter_main.setLayout(self.chapter_layout)

		#self.chapter_info.setContentsMargins(-8,-7,-7,-7)
		#self.chapter_info.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)
		self.chapter_info_view = series.ChapterInfo()
		self.chapter_layout.addWidget(self.chapter_info_view)

		chapter_list_view = series.ChapterView()
		self.chapter_layout.addWidget(chapter_list_view)
		#self.chapter.setCollapsible(0, True)
		#self.chapter.setCollapsible(1, False)

	def init_toolbar(self):
		self.toolbar = QToolBar()
		self.toolbar.setFixedHeight(30)
		self.toolbar.setWindowTitle("Show") # text for the contextmenu
		#self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined?
		self.toolbar.setMovable(False)
		self.toolbar.setFloatable(False)
		#self.toolbar.setIconSize(QSize(20,20))
		self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

		spacer_start = QWidget() # aligns the first actions properly
		spacer_start.setFixedSize(QSize(10, 1))
		self.toolbar.addWidget(spacer_start)

		favourite_view_icon = QIcon(gui_constants.STAR_BTN_PATH)
		favourite_view_action = QAction(favourite_view_icon, "Favourite", self)
		#favourite_view_action.setText("Manga View")
		favourite_view_action.triggered.connect(lambda: self.setCurrentIndex(1)) #need lambda to pass extra args
		self.toolbar.addAction(favourite_view_action)

		catalog_view_icon = QIcon(gui_constants.HOME_BTN_PATH)
		catalog_view_action = QAction(catalog_view_icon, "Library", self)
		#catalog_view_action.setText("Catalog")
		catalog_view_action.triggered.connect(lambda: self.setCurrentIndex(0)) #need lambda to pass extra args
		self.toolbar.addAction(catalog_view_action)
		self.toolbar.addSeparator()

		series_icon = QIcon(gui_constants.PLUS_PATH)
		series_action = QAction(series_icon, "Add series...", self)
		series_action.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit)
		series_menu = QMenu()
		series_menu.addSeparator()
		populate_action = QAction("Populate from folder...", self)
		populate_action.triggered.connect(self.populate)
		series_menu.addAction(populate_action)
		series_action.setMenu(series_menu)
		self.toolbar.addAction(series_action)

		spacer_middle = QWidget() # aligns buttons to the right
		spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
		self.toolbar.addWidget(spacer_middle)
		
		self.search_bar = QLineEdit()
		self.search_bar.setPlaceholderText("Search title, artist, genres")
		self.search_bar.setMaximumWidth(200)
		self.toolbar.addWidget(self.search_bar)
		self.toolbar.addSeparator()
		settings_icon = QIcon(gui_constants.SETTINGS_PATH)
		settings_action = QAction(settings_icon, "Set&tings", self)
		self.toolbar.addAction(settings_action)
		self.addToolBar(self.toolbar)
		
		spacer_end = QWidget() # aligns About action properly
		spacer_end.setFixedSize(QSize(10, 1))
		self.toolbar.addWidget(spacer_end)

	def setCurrentIndex(self, number, index=None):
		"""Changes the current display view.
		Params:
			number <- int (0 for manga view, 1 for chapter view
		Optional:
			index <- QModelIndex for chapter view
		Note: 0-based indexing
		"""
		if index is not None:
			self.chapter_info_view.display_manga(index)
			self.display.setCurrentIndex(number)
		else:
			self.display.setCurrentIndex(number)

	# TODO: Improve this so that it adds to the series dialog,
	# so user can edit data before inserting (make it a choice)
	def populate(self):
		"Populates the database with series from local drive'"
		msgbox = QMessageBox()
		msgbox.setText("<font color='red'><b>Use with care.</b></font> Choose a folder containing all your series'.")
		msgbox.setInformativeText("Oniichan, are you sure you want to do this?")
		msgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
		msgbox.setDefaultButton(QMessageBox.No)
		if msgbox.exec() == QMessageBox.Yes:
			path = QFileDialog.getExistingDirectory(None, "Choose a folder containing your series'")
			if len(path) is not 0:
				data_thread = QThread()
				loading_thread = QThread()
				loading = misc.Loading()

				if not loading.ON:
					misc.Loading.ON = True
					fetch_instance = fetch.Fetch()
					fetch_instance.series_path = path
					loading.show()

					def finished(status):
						if status:
							self.manga_list_view.series_model.populate_data()
							# TODO: make it spawn a dialog instead (from utils.py or misc.py)
							if loading.progress.maximum() == loading.progress.value():
								misc.Loading.ON = False
								loading.hide()
							data_thread.quit
						else:
							loading.setText("<font color=red>An error occured. Try restarting..</font>")
							loading.progress.setStyleSheet("background-color:red")
							data_thread.quit

					def fetch_deleteLater():
						try:
							fetch_instance.deleteLater
						except NameError:
							pass

					def thread_deleteLater(): #NOTE: Isn't this bad?
						data_thread.deleteLater

					def a_progress(prog):
						loading.progress.setValue(prog)
						loading.setText("Searching on local disk...\n(Will take a while on first time)")

					fetch_instance.moveToThread(data_thread)
					fetch_instance.DATA_COUNT.connect(loading.progress.setMaximum)
					fetch_instance.PROGRESS.connect(a_progress)
					data_thread.started.connect(fetch_instance.local)
					fetch_instance.FINISHED.connect(finished)
					fetch_instance.FINISHED.connect(fetch_deleteLater)
					fetch_instance.FINISHED.connect(thread_deleteLater)
					data_thread.start()
Esempio n. 35
0
class ProjectTreeColumn(QDialog):

    # Signalsnproject =
    dockWidget = pyqtSignal('PyQt_PyObject')
    undockWidget = pyqtSignal()
    changeTitle = pyqtSignal('PyQt_PyObject', 'QString')
    updateLocator = pyqtSignal()
    activeProjectChanged = pyqtSignal()

    def __init__(self, parent=None):
        super(ProjectTreeColumn, self).__init__(parent)
        vbox = QVBoxLayout(self)
        vbox.setSizeConstraint(QVBoxLayout.SetDefaultConstraint)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)
        self._buttons = []
        frame = QFrame()
        frame.setObjectName("actionbar")
        box = QVBoxLayout(frame)
        box.setContentsMargins(1, 1, 1, 1)
        box.setSpacing(0)

        self._combo_project = QComboBox()
        self._combo_project.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Fixed)
        self._combo_project.setSizeAdjustPolicy(
            QComboBox.AdjustToMinimumContentsLengthWithIcon)
        self._combo_project.setObjectName("combo_projects")
        box.addWidget(self._combo_project)
        vbox.addWidget(frame)
        self._combo_project.setContextMenuPolicy(Qt.CustomContextMenu)
        self._projects_area = QStackedLayout()
        logger.debug("This is the projects area")
        vbox.addLayout(self._projects_area)

        # Empty widget
        self._empty_proj = QLabel(translations.TR_NO_PROJECTS)
        self._empty_proj.setAlignment(Qt.AlignCenter)
        self._empty_proj.setAutoFillBackground(True)
        self._empty_proj.setBackgroundRole(QPalette.Base)
        self._projects_area.addWidget(self._empty_proj)
        self._projects_area.setCurrentWidget(self._empty_proj)

        self.projects = []

        self._combo_project.activated.connect(
            self._change_current_project)
        self._combo_project.customContextMenuRequested[
            'const QPoint&'].connect(self.context_menu_for_root)

        connections = (
            {
                "target": "main_container",
                "signal_name": "addToProject",
                "slot": self._add_file_to_project
            },
            {
                "target": "main_container",
                "signal_name": "showFileInExplorer",
                "slot": self._show_file_in_explorer
            },
        )
        IDE.register_service('projects_explorer', self)
        IDE.register_signals('projects_explorer', connections)
        ExplorerContainer.register_tab(translations.TR_TAB_PROJECTS, self)

        # FIXME: Should have a ninja settings object that stores tree state
        # FIXME: Or bettter, application data object
        # TODO: check this:
        # self.connect(ide, SIGNAL("goingDown()"),
        #    self.tree_projects.shutdown)

        # def close_project_signal():
        #    self.emit(SIGNAL("updateLocator()"))

    def install_tab(self):
        ide = IDE.get_service('ide')
        ui_tools.install_shortcuts(self, actions.PROJECTS_TREE_ACTIONS, ide)
        ide.goingDown.connect(self._on_ide_going_down)

    def _on_ide_going_down(self):
        """Save some settings before close"""
        if self.current_tree is None:
            return
        ds = IDE.data_settings()
        show_filesize = not bool(self.current_tree.isColumnHidden(1))
        ds.setValue("projectsExplorer/showFileSize", show_filesize)

    def load_session_projects(self, projects):
        for project in projects:
            if os.path.exists(project):
                self._open_project_folder(project)

    def open_project_folder(self, folderName=None):
        if settings.WORKSPACE:
            directory = settings.WORKSPACE
        else:
            directory = os.path.expanduser("~")

        if folderName is None:
            folderName = QFileDialog.getExistingDirectory(
                self, translations.TR_OPEN_PROJECT_DIRECTORY, directory)
            logger.debug("Choosing Foldername")
        if folderName:
            if not file_manager.folder_exists(folderName):
                QMessageBox.information(
                    self,
                    translations.TR_PROJECT_NONEXIST_TITLE,
                    translations.TR_PROJECT_NONEXIST % folderName)
                return
            logger.debug("Opening %s" % folderName)
            for p in self.projects:
                if p.project.path == folderName:
                    QMessageBox.information(
                        self,
                        translations.TR_PROJECT_PATH_ALREADY_EXIST_TITLE,
                        translations.TR_PROJECT_PATH_ALREADY_EXIST
                        % folderName)
                    return
            self._open_project_folder(folderName)

    def _open_project_folder(self, folderName):
        ninjaide = IDE.get_service("ide")
        # TODO: handle exception when .nja file is empty
        project = NProject(folderName)
        qfsm = ninjaide.filesystem.open_project(project)
        if qfsm:
            self.add_project(project)
            self.save_recent_projects(folderName)
            # FIXME: show editor area?
            # main_container = IDE.get_service('main_container')
            # if main_container:
            #    main_container.show_editor_area()
            if len(self.projects) > 1:
                title = "%s (%s)" % (
                    translations.TR_TAB_PROJECTS, len(self.projects))
            else:
                title = translations.TR_TAB_PROJECTS
            self.changeTitle.emit(self, title)

    def _add_file_to_project(self, path):
        """Add the file for 'path' in the project the user choose here."""
        if self._projects_area.count() > 0:
            path_project = [self.current_project]
            _add_to_project = add_to_project.AddToProject(path_project, self)
            _add_to_project.exec_()
            if not _add_to_project.path_selected:
                return
            main_container = IDE.get_service('main_container')
            if not main_container:
                return
            editorWidget = main_container.get_current_editor()
            if not editorWidget.file_path:
                name = QInputDialog.getText(
                    None,
                    translations.TR_ADD_FILE_TO_PROJECT,
                    translations.TR_FILENAME + ": ")[0]
                if not name:
                    QMessageBox.information(
                        self,
                        translations.TR_INVALID_FILENAME,
                        translations.TR_INVALID_FILENAME_ENTER_A_FILENAME)
                    return
            else:
                name = file_manager.get_basename(editorWidget.file_path)
            new_path = file_manager.create_path(
                _add_to_project.path_selected, name)
            ide_srv = IDE.get_service("ide")
            old_file = ide_srv.get_or_create_nfile(path)
            new_file = old_file.save(editorWidget.text(), new_path)
            # FIXME: Make this file replace the original in the open tab
        else:
            pass
            # Message about no project

    def _show_file_in_explorer(self, path):
        '''Iterate through the list of available projects and show
        the current file in the explorer view for the first
        project that contains it (i.e. if the same file is
        included in multiple open projects, the path will be
        expanded for the first project only).
        Note: This slot is connected to the main container's
        "showFileInExplorer(QString)" signal.'''
        central = IDE.get_service('central_container')
        if central and not central.is_lateral_panel_visible():
            return
        for project in self.projects:
            index = project.model().index(path)
            if index.isValid():
                # This highlights the index in the tree for us
                project.scrollTo(index, QAbstractItemView.EnsureVisible)
                project.setCurrentIndex(index)
                break

    def add_project(self, project):
        if project not in self.projects:
            self._combo_project.addItem(project.name)
            tooltip = utils.path_with_tilde_homepath(project.path)
            self._combo_project.setToolTip(tooltip)
            index = self._combo_project.count() - 1
            self._combo_project.setItemData(index, project)
            ptree = TreeProjectsWidget(project)
            self._projects_area.addWidget(ptree)
            ptree.closeProject['PyQt_PyObject'].connect(self._close_project)
            pmodel = project.model
            ptree.setModel(pmodel)
            pindex = pmodel.index(pmodel.rootPath())
            ptree.setRootIndex(pindex)
            self.projects.append(ptree)
            self._projects_area.setCurrentWidget(ptree)  # Can be empty widget
            self._combo_project.setCurrentIndex(index)

        # FIXME: improve?
        # if len(self.projects) == 1:
        #     self._combo_project.currentIndexChanged[int].connect(
        #         self._change_current_project)

    def _close_project(self, widget):
        """Close the project related to the tree widget."""
        index = self._combo_project.currentIndex()
        self.projects.remove(widget)
        # index + 1 is necessary because the widget
        # with index 0 is the empty widget
        self._projects_area.takeAt(index + 1)
        self._combo_project.removeItem(index)
        index = self._combo_project.currentIndex()
        self._projects_area.setCurrentIndex(index + 1)
        ninjaide = IDE.get_service('ide')
        ninjaide.filesystem.close_project(widget.project.path)
        widget.deleteLater()
        if len(self.projects) > 1:
            title = "%s (%s)" % (
                translations.TR_TAB_PROJECTS, len(self.projects))
        else:
            title = translations.TR_TAB_PROJECTS
        self.changeTitle.emit(self, title)
        self.updateLocator.emit()

    def _change_current_project(self, index):
        nproject = self._combo_project.itemData(index)

        ninjaide = IDE.get_service("ide")
        projects = ninjaide.get_projects()
        for project in projects.values():
            if project == nproject:
                nproject.is_current = True
            else:
                project.is_current = False
        self._projects_area.setCurrentIndex(index + 1)
        self.activeProjectChanged.emit()

    def close_opened_projects(self):
        for project in reversed(self.projects):
            self._close_project(project)

    def save_project(self):
        """Save all the opened files that belongs to the actual project."""
        if self.current_project is not None:
            path = self.current_project.path
            main_container = IDE.get_service('main_container')
            if path and main_container:
                main_container.save_project(path)

    def create_new_project(self):
        wizard = new_project_manager.NewProjectManager(self)
        wizard.show()

    @property
    def current_project(self):
        project = None
        if self._projects_area.count() > 0 and self.current_tree is not None:
            project = self.current_tree.project
        return project

    @property
    def current_tree(self):
        tree = None
        widget = self._projects_area.currentWidget()
        if isinstance(widget, TreeProjectsWidget):
            tree = widget
        return tree

    def set_current_item(self, path):
        if self.current_project is not None:
            self.current_tree.set_current_item(path)

    def save_recent_projects(self, folder):
        settings = IDE.data_settings()
        recent_project_list = settings.value('recentProjects', {})
        # if already exist on the list update the date time
        projectProperties = json_manager.read_ninja_project(folder)
        name = projectProperties.get('name', '')
        description = projectProperties.get('description', '')

        if not name:
            name = file_manager.get_basename(folder)

        if not description:
            description = translations.TR_NO_DESCRIPTION

        if folder in recent_project_list:
            properties = recent_project_list[folder]
            properties["lastopen"] = QDateTime.currentDateTime()
            properties["name"] = name
            properties["description"] = description
            recent_project_list[folder] = properties
        else:
            recent_project_list[folder] = {
                "name": name,
                "description": description,
                "isFavorite": False, "lastopen": QDateTime.currentDateTime()}
            # if the length of the project list it's high that 10 then delete
            # the most old
            # TODO: add the length of available projects to setting
            if len(recent_project_list) > MAX_RECENT_PROJECTS:
                del recent_project_list[self.find_most_old_open(
                    recent_project_list)]
        settings.setValue('recentProjects', recent_project_list)

    def find_most_old_open(self, recent_project_list):
        listFounder = []
        for recent_project_path, content in list(recent_project_list.items()):
            listFounder.append((recent_project_path, int(
                content["lastopen"].toString("yyyyMMddHHmmzzz"))))
        listFounder = sorted(listFounder, key=lambda date: listFounder[1],
                             reverse=True)   # sort by date last used
        return listFounder[0][0]

    def reject(self):
        if self.parent() is None:
            self.dockWidget.emit(self)

    def closeEvent(self, event):
        self.dockWidget.emit(self)
        event.ignore()

    def context_menu_for_root(self):
        menu = QMenu(self)
        if self.current_tree is None:
            # No projects
            return
        path = self.current_tree.project.path
        # Reset index
        self.current_tree.setCurrentIndex(QModelIndex())

        action_add_file = menu.addAction(QIcon(":img/new"),
                                         translations.TR_ADD_NEW_FILE)
        action_add_folder = menu.addAction(QIcon(
            ":img/openProj"), translations.TR_ADD_NEW_FOLDER)
        action_create_init = menu.addAction(translations.TR_CREATE_INIT)
        menu.addSeparator()
        action_run_project = menu.addAction(translations.TR_RUN_PROJECT)
        action_properties = menu.addAction(translations.TR_PROJECT_PROPERTIES)
        action_show_file_size = menu.addAction(translations.TR_SHOW_FILESIZE)
        menu.addSeparator()
        action_close = menu.addAction(translations.TR_CLOSE_PROJECT)

        # Connections
        action_add_file.triggered.connect(
            lambda: self.current_tree._add_new_file(path))
        action_add_folder.triggered.connect(
            lambda: self.current_tree._add_new_folder(path))
        action_create_init.triggered.connect(self.current_tree._create_init)
        action_run_project.triggered.connect(
            self.current_tree._execute_project)
        action_properties.triggered.connect(
            self.current_tree.open_project_properties)
        action_close.triggered.connect(self.current_tree._close_project)
        action_show_file_size.triggered.connect(
            self.current_tree.show_filesize_info)

        # menu for the project
        for m in self.current_tree.extra_menus_by_scope['project']:
            if isinstance(m, QMenu):
                menu.addSeparator()
                menu.addMenu(m)

        # show the menu!
        menu.exec_(QCursor.pos())
Esempio n. 36
0
class ComboEditor(QDialog):
    
    closeSplit = pyqtSignal(QWidget)#closeSplit
    allFilesClosed = pyqtSignal()
    splitEditor = pyqtSignal("QWidget*", "QWidget*", bool)
    recentTabsModified = pyqtSignal()
    aboutToCloseComboEditor = pyqtSignal()

    class NAVIGATE:
        prev = 0
        next = 1

    Q_ENUMS(NAVIGATE)

    def __init__(self, original=False):
        super(ComboEditor, self).__init__(None, Qt.WindowStaysOnTopHint)
        self.__original = original
        self.__undocked = []
        self._symbols_index = []
        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)

        self.bar = ActionBar(main_combo=original)
        vbox.addWidget(self.bar)

        self.stacked = QStackedLayout()
        vbox.addLayout(self.stacked)

        self._main_container = IDE.get_service('main_container')

        if not self.__original:
            self._main_container.fileOpened.connect(self._file_opened_by_main)

        self.bar.combo.showComboSelector.connect(self._main_container.change_tab)
        self.bar.changeCurrent.connect(self._set_current)
        self.bar.editorSplited.connect(self.split_editor)
        self.bar.runFile[str].connect(self._run_file)
        self.bar.closeFile.connect(lambda: self.closeSplit.emit(self))
        self.bar.addToProject[str].connect(self._add_to_project)
        self.bar.showFileInExplorer.connect(self._show_file_in_explorer)
        self.bar.goToSymbol.connect(self._go_to_symbol)
        self.bar.undockEditor.connect(self.undock_editor)
        self.bar.reopenTab[str].connect(self._main_container.open_file)
        self.bar.recentTabsModified.connect(self._main_container.recent_files_changed)
        self.bar.code_navigator.btnPrevious.clicked['bool'].connect(lambda: self._navigate_code(self.NAVIGATE.prev))
        self.bar.code_navigator.btnNext.clicked['bool'].connect(lambda: self._navigate_code(self.NAVIGATE.prev))

    def _navigate_code(self, val):
        op = self.bar.code_navigator.operation
        self._main_container.navigate_code_history(val, op)

    def currentWidget(self):
        return self.stacked.currentWidget()

    def setFocus(self):
        super(ComboEditor, self).setFocus()
        w = self.stacked.currentWidget()
        if w:
            w.setFocus()
        self._editor_with_focus()

    def _file_opened_by_main(self, path):
        index = self.stacked.currentIndex()
        ninjaide = IDE.getInstance()
        editable = ninjaide.get_or_create_editable(path)
        print("_file_opened_by_main", editable)
        self.add_editor(editable)
        self.bar.set_current_by_index(index)
        if index == -1:
            self.bar.set_current_by_index(0)

    def add_editor(self, neditable, keep_index=False):
        """Add Editor Widget to the UI area."""
        if neditable.editor:
            if self.__original:
                editor = neditable.editor
            else:
                editor = self._main_container.create_editor_from_editable(
                    neditable)
            index = self.stacked.currentIndex()
            self.stacked.addWidget(editor)
            self.bar.add_item(neditable.display_name, neditable)
            if keep_index:
                self.bar.set_current_by_index(index)

            # Editor Signals
            editor.cursorPositionChanged[int, int].connect(self._update_cursor_position)
            editor.editorFocusObtained.connect(self._editor_with_focus)
            editor.currentLineChanged.connect(self._set_current_symbol)
            editor.modificationChanged['bool'].connect(self._editor_modified)
            neditable.checkersUpdated.connect(self._show_notification_icon)
            neditable.fileSaved.connect(self._update_symbols)
            neditable.fileSaved.connect(self._update_combo_info)

            # Connect file system signals only in the original
            neditable.fileClosing.connect(self._close_file)
            if self.__original:
                neditable.askForSaveFileClosing.connect(self._ask_for_save)
                neditable.fileChanged.connect(self._file_has_been_modified)

            # Load Symbols
            self._load_symbols(neditable)

    def show_combo_file(self):
        print("show_combo_file")
        self.bar.combo.showPopup()

    def show_combo_symbol(self):
        self.bar.symbols_combo.showPopup()

    def unlink_editors(self):
        for index in range(self.stacked.count()):
            widget = self.stacked.widget(index)
            widget.setDocument(QsciDocument())

    def split_editor(self, orientationVertical):
        new_widget = ComboEditor()
        for neditable in self.bar.get_editables():
            print("\nsplit_editor", neditable, new_widget)
            new_widget.add_editor(neditable)
        self.splitEditor.emit(self, new_widget, orientationVertical)

    def undock_editor(self):
        new_combo = ComboEditor()
        new_combo.setWindowTitle("NINJA-IDE")
        self.__undocked.append(new_combo)
        for neditable in self.bar.get_editables():
            print("undock_editor", neditable)
            new_combo.add_editor(neditable)
        new_combo.resize(500, 500)
        new_combo.aboutToCloseComboEditor.connect(self._remove_undock)
        new_combo.show()

    def _remove_undock(self):
        widget = self.sender()
        self.__undocked.remove(widget)

    def close_current_file(self):
        self.bar.about_to_close_file()

    def _close_file(self, neditable):
        index = self.bar.close_file(neditable)
        layoutItem = self.stacked.takeAt(index)
        #neditable.editor.completer.cc.unload_module()
        self._add_to_last_opened(neditable.file_path)
        layoutItem.widget().deleteLater()

        if self.stacked.isEmpty():
            self.allFilesClosed.emit()

    def _add_to_last_opened(self, path):
        if path not in settings.LAST_OPENED_FILES:
            settings.LAST_OPENED_FILES.append(path)
            if len(settings.LAST_OPENED_FILES) > settings.MAX_REMEMBER_TABS:
                self.__lastOpened = self.__lastOpened[1:]
            self.recentTabsModified.emit()

    def _editor_with_focus(self):
        if self._main_container.current_widget is not self:
            self._main_container.current_widget = self
            editor = self.stacked.currentWidget()
            self._main_container.current_editor_changed(
                editor.neditable.file_path)
            self._load_symbols(editor.neditable)
            editor.neditable.update_checkers_display()

    def _ask_for_save(self, neditable):
        val = QMessageBox.No
        fileName = neditable.nfile.file_name
        val = QMessageBox.question(
            self, (self.tr('The file %s was not saved') %
                   fileName),
            self.tr("Do you want to save before closing?"),
            QMessageBox.Yes | QMessageBox.No |
            QMessageBox.Cancel)
        if val == QMessageBox.No:
            neditable.nfile.close(force_close=True)
        elif val == QMessageBox.Yes:
            neditable.ignore_checkers = True
            self._main_container.save_file(neditable.editor)
            neditable.nfile.close()

    def _file_has_been_modified(self, neditable):
        val = QMessageBox.No
        fileName = neditable.file_path
        val = QMessageBox.question(
            self, translations.TR_FILE_HAS_BEEN_MODIFIED,
            "%s%s" % (fileName, translations.TR_FILE_MODIFIED_OUTSIDE),
            QMessageBox.Yes | QMessageBox.No)
        if val == QMessageBox.Yes:
            neditable.reload_file()

    def _run_file(self, path):
        self._main_container.run_file(path)

    def _add_to_project(self, path):
        self._main_container._add_to_project(path)

    def _show_file_in_explorer(self, path):
        '''Connected to ActionBar's showFileInExplorer(QString)
        signal, forwards the file path on to the main container.'''
        self._main_container._show_file_in_explorer(path)

    def set_current(self, neditable):
        if neditable:
            self.bar.set_current_file(neditable)

    def _set_current(self, neditable, index):
        if neditable:
            self.stacked.setCurrentIndex(index)
            editor = self.stacked.currentWidget()
            self._update_cursor_position(ignore_sender=True)
            editor.setFocus()
            self._main_container.current_editor_changed(
                neditable.file_path)
            self._load_symbols(neditable)
            self._show_file_in_explorer(neditable.file_path)
            neditable.update_checkers_display()

    def widget(self, index):
        return self.stacked.widget(index)

    def count(self):
        """Return the number of editors opened."""
        return self.stacked.count()

    def _update_cursor_position(self, line=0, col=0, ignore_sender=False):
        obj = self.sender()
        editor = self.stacked.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            line += 1
            self.bar.update_line_col(line, col)

    def _set_current_symbol(self, line, ignore_sender=False):
        obj = self.sender()
        editor = self.stacked.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            index = bisect.bisect(self._symbols_index, line)
            if (index >= len(self._symbols_index) or
                    self._symbols_index[index] > (line + 1)):
                index -= 1
            self.bar.set_current_symbol(index)

    def _editor_modified(self, value):
        obj = self.sender()
        neditable = obj.neditable
        if value:
            text = "\u2022 %s" % neditable.display_name
            self.bar.update_item_text(neditable, text)
        else:
            self.bar.update_item_text(neditable, neditable.display_name)

    def _go_to_symbol(self, index):
        print("_go_to_symbol in index:", index)
        line = self._symbols_index[index]
        editor = self.stacked.currentWidget()
        editor.go_to_line(line)

    def _update_symbols(self, neditable):
        editor = self.stacked.currentWidget()
        # Check if it's current to avoid signals from other splits.
        if editor == neditable.editor:
            self._load_symbols(neditable)

    def _update_combo_info(self, neditable):
        self.bar.update_item_text(neditable, neditable.display_name)
        self._main_container.current_editor_changed(neditable.file_path)

    def _load_symbols(self, neditable):
        symbols_handler = handlers.get_symbols_handler('py')
        source = neditable.editor.text()
        source = source.encode(neditable.editor.encoding)
        symbols, symbols_simplified = symbols_handler.obtain_symbols(
            source, simple=True)
        self._symbols_index = sorted(symbols_simplified.keys())
        symbols_simplified = sorted(
            list(symbols_simplified.items()), key=lambda x: x[0])
        self.bar.add_symbols(symbols_simplified)
        line, _ = neditable.editor.getCursorPosition()
        self._set_current_symbol(line, True)
        tree_symbols = IDE.get_service('symbols_explorer')
        tree_symbols.update_symbols_tree(symbols, neditable.file_path)

    def _show_notification_icon(self, neditable):
        checkers = neditable.sorted_checkers
        icon = QIcon()
        for items in checkers:
            checker, color, _ = items
            if checker.checks:
                if isinstance(checker.checker_icon, int):
                    icon = self.style().standardIcon(checker.checker_icon)
                elif isinstance(checker.checker_icon, str):
                    icon = QIcon(checker.checker_icon)
                break
        self.bar.update_item_icon(neditable, icon)

    def show_menu_navigation(self):
        self.bar.code_navigator.show_menu_navigation()

    def closeEvent(self, event):
        self.aboutToCloseComboEditor.emit()
        super(ComboEditor, self).closeEvent(event)

    def reject(self):
        if not self.__original:
            super(ComboEditor, self).reject()
Esempio n. 37
0
class AbstractTemplateWidget(QFrame):

    """
    TemplateWidget is used in reports and options tab.
    So it needs several common methods and common layout.
    But behavior is different. it should be defined in children classes.
    """

    def __init__(self, main_window, items):
        super().__init__()

        self.items = items
        self.visible_items = []
        self.layout = QStackedLayout()
        self.menu_layout = QVBoxLayout()
        self.templates_layout = QStackedLayout()
        self.showEvent = self._get_show_event(main_window)
        self.menu_wrapper = QVBoxLayout()

        try:
            self.ACTION_BTN_ICON
        except AttributeError:
            self.ACTION_BTN_ICON = ""

        self.setLayout(self.layout)

        self.layout.addWidget(self._get_static_widgets())

    def _get_static_widgets(self):
        """
        Create layout that does not depend on content.
        """

        hbox = QHBoxLayout()
        self.menu_wrapper.addWidget(utils.get_scrollable(self.menu_layout))
        hbox.addLayout(self.menu_wrapper, stretch=30)
        hbox.addLayout(self.templates_layout, stretch=70)
        widget = QWidget()
        widget.setLayout(hbox)
        widget.setGraphicsEffect(utils.get_shadow())
        return widget

    def _iterate_items(self):
        """
        Filter items if they has no values.
        """
        pass

    def hideEvent(self, event):
        """
        Clear menu and templates.
        """

        utils.clear_layout(self.menu_layout)
        utils.clear_layout(self.templates_layout)

    def _get_show_event(self, main_window):
        """
        Update templates list and re-select them.
        """

        def show_event(event):
            utils.clear_layout(self.menu_layout)
            utils.clear_layout(self.templates_layout)

            self.visible_items = self._iterate_items()
            self._show_menu()
            self._show_templates()
            if not self.layout.currentIndex():
                main_window.communication.action_button_toggle.emit(
                    bool(self.visible_items), self.ACTION_BTN_ICON, self.action_btn_function
                )

        return show_event

    def _show_menu(self):
        """
        Update menu on showEvent.
        """

        for i, item in enumerate(self.visible_items):
            b = QRadioButton(self._get_button_name(item))
            b.setChecked(i == 0)
            b.clicked.connect(functools.partial(self.templates_layout.setCurrentIndex, i))
            b.setObjectName("menu_button")
            self.menu_layout.addWidget(b)

        if not self.visible_items:
            self.menu_layout.addStretch()
            l = QLabel("Чтобы создать отчет\nначните заполнять данные")
            l.setAlignment(Qt.AlignCenter)
            self.menu_layout.addWidget(l)

        self.menu_layout.addStretch()

    def _show_templates(self):
        """
        Update templates on shoeEvent.
        """

        cols = 3
        templates = template_module.Template.get_all()

        for j, item in enumerate(self.visible_items):
            if not templates[item.id]:
                l = QLabel("Нет шаблонов для данного объекта\nУправлять шаблонами можно на вкладке настроек")
                l.setAlignment(Qt.AlignCenter)
                self.templates_layout.addWidget(l)
                continue
            layouts = [QVBoxLayout() for _ in range(cols)]
            for i, each in enumerate(templates[item.id]):
                b = QRadioButton(each.name)
                b.setChecked(item.template == each)
                b.clicked.connect(functools.partial(self._template_clicked, j, each))
                b.mouseDoubleClickEvent = functools.partial(self.open_template_edit_widget, j, each)
                layouts[i % cols].addWidget(b)

            wrapper = QHBoxLayout()
            for each in layouts:
                each.addStretch()
                wrapper.addLayout(each, stretch=int(100 / cols))
            self.templates_layout.addWidget(utils.get_scrollable(wrapper))

    def _template_selected(self, index, template):

        """
        Change menu item name.
        Add template for the item.
        """

        self.visible_items[index].template = template
        buttons = self.findChildren(QRadioButton, name="menu_button")
        buttons[index].setText(self._get_button_name(self.visible_items[index]))
        for i in range(len(self.visible_items)):
            ind = (i + index) % len(self.visible_items)
            if not self.visible_items[ind].template:
                self.templates_layout.setCurrentIndex(ind)
                buttons[ind].setChecked(True)
                buttons[index].setChecked(False)
                return

    def _get_button_name(self, item):
        pass

    def _double_click(self, index, template, event):
        pass

    def _template_clicked(self, index, template):
        pass

    def action_btn_function(self, event):
        pass

    def open_template_edit_widget(self, index, template, event):
        pass
Esempio n. 38
0
class SettingsDialog(QWidget):
	"A settings dialog"
	scroll_speed_changed = pyqtSignal()
	def __init__(self, parent=None):
		super().__init__(parent, flags=Qt.Window)
		self.resize(700, 500)
		self.show()
		self.restore_values()
		self.initUI()

	def initUI(self):
		main_layout = QVBoxLayout()
		sub_layout = QHBoxLayout()
		# Left Panel
		left_panel = QListWidget()
		left_panel.setViewMode(left_panel.ListMode)
		#left_panel.setIconSize(QSize(40,40))
		left_panel.setTextElideMode(Qt.ElideRight)
		left_panel.setMaximumWidth(200)
		left_panel.itemClicked.connect(self.change)
		#web.setText('Web')
		self.application = QListWidgetItem()
		self.application.setText('Application')
		self.web = QListWidgetItem()
		self.web.setText('Web')
		self.visual = QListWidgetItem()
		self.visual.setText('Visual')
		self.advanced = QListWidgetItem()
		self.advanced.setText('Advanced')
		self.about = QListWidgetItem()
		self.about.setText('About')

		#main.setIcon(QIcon(os.path.join(gui_constants.static_dir, 'plus2.png')))
		left_panel.addItem(self.application)
		left_panel.addItem(self.web)
		left_panel.addItem(self.visual)
		left_panel.addItem(self.advanced)
		left_panel.addItem(self.about)
		left_panel.setMaximumWidth(100)

		# right panel
		self.right_panel = QStackedLayout()
		self.init_right_panel()

		# bottom
		bottom_layout = QHBoxLayout()
		ok_btn = QPushButton('Ok')
		ok_btn.clicked.connect(self.accept)
		cancel_btn = QPushButton('Cancel')
		cancel_btn.clicked.connect(self.close)
		info_lbl = QLabel()
		info_lbl.setText('<a href="https://github.com/Pewpews/happypanda">'+
				   'Visit GitHub Repo</a> | Options marked with * requires application restart.')
		info_lbl.setTextFormat(Qt.RichText)
		info_lbl.setTextInteractionFlags(Qt.TextBrowserInteraction)
		info_lbl.setOpenExternalLinks(True)
		self.spacer = QWidget()
		self.spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
		bottom_layout.addWidget(info_lbl, 0, Qt.AlignLeft)
		bottom_layout.addWidget(self.spacer)
		bottom_layout.addWidget(ok_btn, 0, Qt.AlignRight)
		bottom_layout.addWidget(cancel_btn, 0, Qt.AlignRight)

		sub_layout.addWidget(left_panel)
		sub_layout.addLayout(self.right_panel)
		main_layout.addLayout(sub_layout)
		main_layout.addLayout(bottom_layout)

		self.restore_options()

		self.setLayout(main_layout)
		self.setWindowTitle('Settings')


	def change(self, item):
		def curr_index(index):
			if index != self.right_panel.currentIndex():
				self.right_panel.setCurrentIndex(index)
		if item == self.application:
			curr_index(self.application_index)
		elif item == self.web:
			curr_index(self.web_index)
		elif item == self.visual:
			curr_index(self.visual_index)
		elif item == self.advanced:
			curr_index(self.advanced_index)
		elif item == self.about:
			curr_index(self.about_index)

	def restore_values(self):
		#Web
		self.exprops = settings.ExProperties()

		# Visual
		self.high_quality_thumbs = gui_constants.HIGH_QUALITY_THUMBS
		self.popup_width = gui_constants.POPUP_WIDTH
		self.popup_height = gui_constants.POPUP_HEIGHT
		self.style_sheet = gui_constants.user_stylesheet_path

		# Advanced
		self.scroll_speed = gui_constants.SCROLL_SPEED
		self.cache_size = gui_constants.THUMBNAIL_CACHE_SIZE
		self.prefetch_item_amnt = gui_constants.PREFETCH_ITEM_AMOUNT

	def restore_options(self):
		# App / Monitor / Misc
		self.enable_monitor.setChecked(gui_constants.ENABLE_MONITOR)
		self.look_new_gallery_startup.setChecked(gui_constants.LOOK_NEW_GALLERY_STARTUP)
		self.auto_add_new_galleries.setChecked(gui_constants.LOOK_NEW_GALLERY_AUTOADD)
		# App / Monitor / Folders
		for path in gui_constants.MONITOR_PATHS:
			self.add_folder_monitor(path)

		# Web / General
		if 'g.e-hentai' in gui_constants.DEFAULT_EHEN_URL:
			self.default_ehen_url.setChecked(True)
		else:
			self.exhentai_ehen_url.setChecked(True)
		
		self.replace_metadata.setChecked(gui_constants.REPLACE_METADATA)
		self.always_first_hit.setChecked(gui_constants.ALWAYS_CHOOSE_FIRST_HIT)
		self.web_time_offset.setValue(gui_constants.GLOBAL_EHEN_TIME)
		self.continue_a_metadata_fetcher.setChecked(gui_constants.CONTINUE_AUTO_METADATA_FETCHER)
		self.use_jpn_title.setChecked(gui_constants.USE_JPN_TITLE)

		# Web / Exhentai
		self.ipbid_edit.setText(self.exprops.ipb_id)
		self.ipbpass_edit.setText(self.exprops.ipb_pass)

		# Visual / Grid View / Tooltip
		self.grid_tooltip_group.setChecked(gui_constants.GRID_TOOLTIP)
		self.visual_grid_tooltip_title.setChecked(gui_constants.TOOLTIP_TITLE)
		self.visual_grid_tooltip_author.setChecked(gui_constants.TOOLTIP_AUTHOR)
		self.visual_grid_tooltip_chapters.setChecked(gui_constants.TOOLTIP_CHAPTERS)
		self.visual_grid_tooltip_status.setChecked(gui_constants.TOOLTIP_STATUS)
		self.visual_grid_tooltip_type.setChecked(gui_constants.TOOLTIP_TYPE)
		self.visual_grid_tooltip_lang.setChecked(gui_constants.TOOLTIP_LANG)
		self.visual_grid_tooltip_descr.setChecked(gui_constants.TOOLTIP_DESCR)
		self.visual_grid_tooltip_tags.setChecked(gui_constants.TOOLTIP_TAGS)
		self.visual_grid_tooltip_last_read.setChecked(gui_constants.TOOLTIP_LAST_READ)
		self.visual_grid_tooltip_times_read.setChecked(gui_constants.TOOLTIP_TIMES_READ)
		self.visual_grid_tooltip_pub_date.setChecked(gui_constants.TOOLTIP_PUB_DATE)
		self.visual_grid_tooltip_date_added.setChecked(gui_constants.TOOLTIP_DATE_ADDED)
		# Visual / Grid View / Gallery
		self.external_viewer_ico.setChecked(gui_constants.USE_EXTERNAL_PROG_ICO)
		self.gallery_type_ico.setChecked(gui_constants.DISPLAY_GALLERY_TYPE)
		if gui_constants.GALLERY_FONT_ELIDE:
			self.gallery_text_elide.setChecked(True)
		else:
			self.gallery_text_fit.setChecked(True)
		self.font_lbl.setText(gui_constants.GALLERY_FONT[0])
		self.font_size_lbl.setValue(gui_constants.GALLERY_FONT[1])

		def re_enforce(s):
			if s:
				self.search_on_enter.setChecked(True)
		self.search_allow_regex.clicked.connect(re_enforce)

		if gui_constants.SEARCH_ON_ENTER:
			self.search_on_enter.setChecked(True)
		else:
			self.search_every_keystroke.setChecked(True)
		# Visual / Grid View / Colors
		self.grid_label_color.setText(gui_constants.GRID_VIEW_LABEL_COLOR)
		self.grid_title_color.setText(gui_constants.GRID_VIEW_TITLE_COLOR)
		self.grid_artist_color.setText(gui_constants.GRID_VIEW_ARTIST_COLOR)

		# Advanced / Misc / External Viewer
		self.external_viewer_path.setText(gui_constants.EXTERNAL_VIEWER_PATH)

	def accept(self):
		set = settings.set

		# App / Monitor / misc
		gui_constants.ENABLE_MONITOR = self.enable_monitor.isChecked()
		set(gui_constants.ENABLE_MONITOR, 'Application', 'enable monitor')
		gui_constants.LOOK_NEW_GALLERY_STARTUP = self.look_new_gallery_startup.isChecked()
		set(gui_constants.LOOK_NEW_GALLERY_STARTUP, 'Application', 'look new gallery startup')
		gui_constants.LOOK_NEW_GALLERY_AUTOADD = self.auto_add_new_galleries.isChecked()
		set(gui_constants.LOOK_NEW_GALLERY_AUTOADD, 'Application', 'look new gallery autoadd')
		# App / Monitor / folders
		n = self.folders_layout.rowCount()
		paths = ''
		for x in range(n):
			item = self.folders_layout.takeAt(x+1)
			l_edit = item.widget()
			p = l_edit.text()
			if p:
				if x == n-1:
					paths += '{}'.format(p)
				else:
					paths += '{},'.format(p)
		set(paths, 'Application', 'monitor paths')
		gui_constants.MONITOR_PATHS = paths.split(',')

		# Web / General
		if self.default_ehen_url.isChecked():
			gui_constants.DEFAULT_EHEN_URL = 'http://g.e-hentai.org/'
		else:
			gui_constants.DEFAULT_EHEN_URL = 'http://exhentai.org/'
		set(gui_constants.DEFAULT_EHEN_URL, 'Web', 'default ehen url')

		gui_constants.REPLACE_METADATA = self.replace_metadata.isChecked()
		set(gui_constants.REPLACE_METADATA, 'Web', 'replace metadata')

		gui_constants.ALWAYS_CHOOSE_FIRST_HIT = self.always_first_hit.isChecked()
		set(gui_constants.ALWAYS_CHOOSE_FIRST_HIT, 'Web', 'always choose first hit')

		gui_constants.GLOBAL_EHEN_TIME = self.web_time_offset.value()
		set(gui_constants.GLOBAL_EHEN_TIME, 'Web', 'global ehen time offset')

		gui_constants.CONTINUE_AUTO_METADATA_FETCHER = self.continue_a_metadata_fetcher.isChecked()
		set(gui_constants.CONTINUE_AUTO_METADATA_FETCHER, 'Web', 'continue auto metadata fetcher')

		gui_constants.USE_JPN_TITLE = self.use_jpn_title.isChecked()
		set(gui_constants.USE_JPN_TITLE, 'Web', 'use jpn title')

		# Web / ExHentai
		self.exprops.ipb_id = self.ipbid_edit.text()
		self.exprops.ipb_pass = self.ipbpass_edit.text()

		# Visual / Grid View / Tooltip
		gui_constants.GRID_TOOLTIP = self.grid_tooltip_group.isChecked()
		set(gui_constants.GRID_TOOLTIP, 'Visual', 'grid tooltip')
		gui_constants.TOOLTIP_TITLE = self.visual_grid_tooltip_title.isChecked()
		set(gui_constants.TOOLTIP_TITLE, 'Visual', 'tooltip title')
		gui_constants.TOOLTIP_AUTHOR = self.visual_grid_tooltip_author.isChecked()
		set(gui_constants.TOOLTIP_AUTHOR, 'Visual', 'tooltip author')
		gui_constants.TOOLTIP_CHAPTERS = self.visual_grid_tooltip_chapters.isChecked()
		set(gui_constants.TOOLTIP_CHAPTERS, 'Visual', 'tooltip chapters')
		gui_constants.TOOLTIP_STATUS = self.visual_grid_tooltip_status.isChecked()
		set(gui_constants.TOOLTIP_STATUS, 'Visual', 'tooltip status')
		gui_constants.TOOLTIP_TYPE = self.visual_grid_tooltip_type.isChecked()
		set(gui_constants.TOOLTIP_TYPE, 'Visual', 'tooltip type')
		gui_constants.TOOLTIP_LANG = self.visual_grid_tooltip_lang.isChecked()
		set(gui_constants.TOOLTIP_LANG, 'Visual', 'tooltip lang')
		gui_constants.TOOLTIP_DESCR = self.visual_grid_tooltip_descr.isChecked()
		set(gui_constants.TOOLTIP_DESCR, 'Visual', 'tooltip descr')
		gui_constants.TOOLTIP_TAGS = self.visual_grid_tooltip_tags.isChecked()
		set(gui_constants.TOOLTIP_TAGS, 'Visual', 'tooltip tags')
		gui_constants.TOOLTIP_LAST_READ = self.visual_grid_tooltip_last_read.isChecked()
		set(gui_constants.TOOLTIP_LAST_READ, 'Visual', 'tooltip last read')
		gui_constants.TOOLTIP_TIMES_READ = self.visual_grid_tooltip_times_read.isChecked()
		set(gui_constants.TOOLTIP_TIMES_READ, 'Visual', 'tooltip times read')
		gui_constants.TOOLTIP_PUB_DATE = self.visual_grid_tooltip_pub_date.isChecked()
		set(gui_constants.TOOLTIP_PUB_DATE, 'Visual', 'tooltip pub date')
		gui_constants.TOOLTIP_DATE_ADDED = self.visual_grid_tooltip_date_added.isChecked()
		set(gui_constants.TOOLTIP_DATE_ADDED, 'Visual', 'tooltip date added')
		# Visual / Grid View / Gallery
		gui_constants.USE_EXTERNAL_PROG_ICO = self.external_viewer_ico.isChecked()
		set(gui_constants.USE_EXTERNAL_PROG_ICO, 'Visual', 'use external prog ico')
		gui_constants.DISPLAY_GALLERY_TYPE = self.gallery_type_ico.isChecked()
		set(gui_constants.DISPLAY_GALLERY_TYPE, 'Visual', 'display gallery type')
		if self.gallery_text_elide.isChecked():
			gui_constants.GALLERY_FONT_ELIDE = True
		else:
			gui_constants.GALLERY_FONT_ELIDE = False
		set(gui_constants.GALLERY_FONT_ELIDE, 'Visual', 'gallery font elide')
		gui_constants.GALLERY_FONT = (self.font_lbl.text(), self.font_size_lbl.value())
		set(gui_constants.GALLERY_FONT[0], 'Visual', 'gallery font family')
		set(gui_constants.GALLERY_FONT[1], 'Visual', 'gallery font size')
		# Visual / Grid View / Colors
		if self.color_checker(self.grid_title_color.text()):
			gui_constants.GRID_VIEW_TITLE_COLOR = self.grid_title_color.text()
			set(gui_constants.GRID_VIEW_TITLE_COLOR, 'Visual', 'grid view title color')
		if self.color_checker(self.grid_artist_color.text()):
			gui_constants.GRID_VIEW_ARTIST_COLOR = self.grid_artist_color.text()
			set(gui_constants.GRID_VIEW_ARTIST_COLOR, 'Visual', 'grid view artist color')
		if self.color_checker(self.grid_label_color.text()):
			gui_constants.GRID_VIEW_LABEL_COLOR = self.grid_label_color.text()
			set(gui_constants.GRID_VIEW_LABEL_COLOR, 'Visual', 'grid view label color')

		# Advanced / Misc
		# Advanced / Misc / Grid View
		gui_constants.SCROLL_SPEED = self.scroll_speed
		set(self.scroll_speed, 'Advanced', 'scroll speed')
		self.scroll_speed_changed.emit()
		gui_constants.THUMBNAIL_CACHE_SIZE = self.cache_size
		set(self.cache_size[1], 'Advanced', 'cache size')
		QPixmapCache.setCacheLimit(self.cache_size[0]*
							 self.cache_size[1])
		# Advanced / Misc / Search
		gui_constants.ALLOW_SEARCH_REGEX = self.search_allow_regex.isChecked()
		set(gui_constants.ALLOW_SEARCH_REGEX, 'Advanced', 'allow search regex')
		gui_constants.SEARCH_AUTOCOMPLETE = self.search_autocomplete.isChecked()
		set(gui_constants.SEARCH_AUTOCOMPLETE, 'Advanced', 'search autocomplete')
		if self.search_on_enter.isChecked():
			gui_constants.SEARCH_ON_ENTER = True
		else:
			gui_constants.SEARCH_ON_ENTER = False
		set(gui_constants.SEARCH_ON_ENTER, 'Advanced', 'search on enter')

		# Advanced / Misc / External Viewer
		if not self.external_viewer_path.text():
			gui_constants.USE_EXTERNAL_VIEWER = False
			set(False, 'Advanced', 'use external viewer')
		else:
			gui_constants.USE_EXTERNAL_VIEWER = True
			set(True, 'Advanced', 'use external viewer')
			gui_constants._REFRESH_EXTERNAL_VIEWER = True
		gui_constants.EXTERNAL_VIEWER_PATH = self.external_viewer_path.text()
		set(gui_constants.EXTERNAL_VIEWER_PATH,'Advanced', 'external viewer path')

		settings.save()
		self.close()

	def init_right_panel(self):

		#def title_def(title):
		#	title_lbl = QLabel(title)
		#	f = QFont()
		#	f.setPixelSize(16)
		#	title_lbl.setFont(f)
		#	return title_lbl

		# App
		application = QTabWidget()
		self.application_index = self.right_panel.addWidget(application)
		application_general = QWidget()
		application.addTab(application_general, 'General')
		application.setTabEnabled(0, False)

		# App / Monitor
		app_monitor_page = QScrollArea()
		app_monitor_page.setBackgroundRole(QPalette.Base)
		app_monitor_dummy = QWidget()
		app_monitor_page.setWidgetResizable(True)
		app_monitor_page.setWidget(app_monitor_dummy)
		application.addTab(app_monitor_page, 'Monitoring')
		application.setCurrentIndex(1)
		app_monitor_m_l = QVBoxLayout(app_monitor_dummy)
		# App / Monitor / misc
		app_monitor_misc_group = QGroupBox('General *', self)
		app_monitor_m_l.addWidget(app_monitor_misc_group)
		app_monitor_misc_m_l = QFormLayout(app_monitor_misc_group)
		monitor_info = QLabel('Directory monitoring will monitor the specified directories for any'+
						' gallery events. For example if you delete a gallery source in one of your'+
						' monitored directories the application will inform you about it, and ask if'+
						' you want to delete the gallery from the application as well.')
		monitor_info.setWordWrap(True)
		app_monitor_misc_m_l.addRow(monitor_info)
		self.enable_monitor = QCheckBox('Enable directory monitoring')
		app_monitor_misc_m_l.addRow(self.enable_monitor)
		self.look_new_gallery_startup = QGroupBox('Scan for new galleries on startup', self)
		app_monitor_misc_m_l.addRow(self.look_new_gallery_startup)
		self.look_new_gallery_startup.setCheckable(True)
		look_new_gallery_startup_m_l = QVBoxLayout(self.look_new_gallery_startup)
		self.auto_add_new_galleries = QCheckBox('Automatically add found galleries')
		look_new_gallery_startup_m_l.addWidget(self.auto_add_new_galleries)

		# App / Monitor / folders
		app_monitor_group = QGroupBox('Directories *', self)
		app_monitor_m_l.addWidget(app_monitor_group, 1)
		app_monitor_folders_m_l = QVBoxLayout(app_monitor_group)
		app_monitor_folders_add = QPushButton('+')
		app_monitor_folders_add.clicked.connect(self.add_folder_monitor)
		app_monitor_folders_add.setMaximumWidth(20)
		app_monitor_folders_add.setMaximumHeight(20)
		app_monitor_folders_m_l.addWidget(app_monitor_folders_add, 0, Qt.AlignRight)
		self.folders_layout = QFormLayout()
		app_monitor_folders_m_l.addLayout(self.folders_layout)

		# Web
		web = QTabWidget()
		self.web_index = self.right_panel.addWidget(web)
		web_general_page = QScrollArea()
		web_general_page.setBackgroundRole(QPalette.Base)
		web_general_page.setWidgetResizable(True)
		web.addTab(web_general_page, 'General')
		web_general_dummy = QWidget()
		web_general_page.setWidget(web_general_dummy)
		web_general_m_l = QVBoxLayout(web_general_dummy)
		metadata_fetcher_group = QGroupBox('Metadata', self)
		web_general_m_l.addWidget(metadata_fetcher_group)
		metadata_fetcher_m_l = QFormLayout(metadata_fetcher_group)
		self.default_ehen_url = QRadioButton('g.e-hentai.org', metadata_fetcher_group)
		self.exhentai_ehen_url = QRadioButton('exhentai.org', metadata_fetcher_group)
		ehen_url_l = QHBoxLayout()
		ehen_url_l.addWidget(self.default_ehen_url)
		ehen_url_l.addWidget(self.exhentai_ehen_url, 1)
		metadata_fetcher_m_l.addRow('Default URL:', ehen_url_l)
		self.continue_a_metadata_fetcher = QCheckBox('Continue from where auto metadata fetcher left off')
		metadata_fetcher_m_l.addRow(self.continue_a_metadata_fetcher)
		self.use_jpn_title = QCheckBox('Use japanese title')
		metadata_fetcher_m_l.addRow(self.use_jpn_title)
		time_offset_info = QLabel('To avoid getting banned, we need to impose a delay between our requests.'+
							' I have made it so you cannot set the delay lower than the recommended (I don\'t'+
							' want you to get banned, anon).\nSpecify the delay between requests in seconds.')
		time_offset_info.setWordWrap(True)
		self.web_time_offset = QSpinBox()
		self.web_time_offset.setMaximumWidth(40)
		self.web_time_offset.setMinimum(4)
		self.web_time_offset.setMaximum(99)
		metadata_fetcher_m_l.addRow(time_offset_info)
		metadata_fetcher_m_l.addRow('Requests delay in', self.web_time_offset)
		replace_metadata_info = QLabel('When fetching for metadata the new metadata will be appended'+
								 ' to the gallery by default. This means that new data will only be set if'+
								 ' the field was empty. There is however a special case for namespace & tags.'+
								 ' We go through all the new namespace & tags to only add those that'+
								 ' do not already exists.\n\nEnabling this option makes it so that a gallery\'s old data'+
								 ' are deleted and replaced with the new data.')
		replace_metadata_info.setWordWrap(True)
		self.replace_metadata = QCheckBox('Replace old metadata with new metadata')
		metadata_fetcher_m_l.addRow(replace_metadata_info)
		metadata_fetcher_m_l.addRow(self.replace_metadata)
		first_hit_info = QLabel('By default, you get to choose which gallery to extract metadata from when'+
						  ' there is more than one gallery found when searching.\n'+
						  'Enabling this option makes it choose the first hit, saving you from moving your mouse.')
		first_hit_info.setWordWrap(True)
		self.always_first_hit = QCheckBox('Always choose first hit')
		metadata_fetcher_m_l.addRow(first_hit_info)
		metadata_fetcher_m_l.addRow(self.always_first_hit)

		# Web / Exhentai
		exhentai_page = QWidget()
		web.addTab(exhentai_page, 'ExHentai')
		ipb_layout = QFormLayout()
		exhentai_page.setLayout(ipb_layout)
		self.ipbid_edit = QLineEdit()
		self.ipbpass_edit = QLineEdit()
		exh_tutorial = QLabel(gui_constants.EXHEN_COOKIE_TUTORIAL)
		exh_tutorial.setTextFormat(Qt.RichText)
		ipb_layout.addRow('IPB Member ID:', self.ipbid_edit)
		ipb_layout.addRow('IPB Pass Hash:', self.ipbpass_edit)
		ipb_layout.addRow(exh_tutorial)

		# Visual
		visual = QTabWidget()
		self.visual_index = self.right_panel.addWidget(visual)
		visual_general_page = QWidget()
		visual.addTab(visual_general_page, 'General')

		grid_view_general_page = QWidget()
		visual.addTab(grid_view_general_page, 'Grid View')
		grid_view_layout = QVBoxLayout()
		grid_view_layout.addWidget(QLabel('Options marked with * requires application restart'),
						   0, Qt.AlignTop)
		grid_view_general_page.setLayout(grid_view_layout)
		# grid view
		# grid view / tooltip
		self.grid_tooltip_group = QGroupBox('Tooltip', grid_view_general_page)
		self.grid_tooltip_group.setCheckable(True)
		grid_view_layout.addWidget(self.grid_tooltip_group, 0, Qt.AlignTop)
		grid_tooltip_layout = QFormLayout()
		self.grid_tooltip_group.setLayout(grid_tooltip_layout)
		grid_tooltip_layout.addRow(QLabel('Control what is'+
									' displayed in the tooltip'))
		grid_tooltips_hlayout = FlowLayout()
		grid_tooltip_layout.addRow(grid_tooltips_hlayout)
		self.visual_grid_tooltip_title = QCheckBox('Title')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_title)
		self.visual_grid_tooltip_author = QCheckBox('Author')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_author)
		self.visual_grid_tooltip_chapters = QCheckBox('Chapters')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_chapters)
		self.visual_grid_tooltip_status = QCheckBox('Status')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_status)
		self.visual_grid_tooltip_type = QCheckBox('Type')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_type)
		self.visual_grid_tooltip_lang = QCheckBox('Language')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_lang)
		self.visual_grid_tooltip_descr = QCheckBox('Description')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_descr)
		self.visual_grid_tooltip_tags = QCheckBox('Tags')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_tags)
		self.visual_grid_tooltip_last_read = QCheckBox('Last read')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_last_read)
		self.visual_grid_tooltip_times_read = QCheckBox('Times read')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_times_read)
		self.visual_grid_tooltip_pub_date = QCheckBox('Publication Date')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_pub_date)
		self.visual_grid_tooltip_date_added = QCheckBox('Date added')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_date_added)
		# grid view / gallery
		grid_gallery_group = QGroupBox('Gallery', grid_view_general_page)
		grid_view_layout.addWidget(grid_gallery_group, 0, Qt.AlignTop)
		grid_gallery_main_l = QFormLayout()
		grid_gallery_main_l.setFormAlignment(Qt.AlignLeft)
		grid_gallery_group.setLayout(grid_gallery_main_l)
		grid_gallery_display = FlowLayout()
		grid_gallery_main_l.addRow('Display on gallery:', grid_gallery_display)
		self.external_viewer_ico = QCheckBox('External Viewer')
		self.external_viewer_ico.setDisabled(True)
		grid_gallery_display.addWidget(self.external_viewer_ico)
		self.gallery_type_ico = QCheckBox('File Type')
		self.gallery_type_ico.setDisabled(True)
		grid_gallery_display.addWidget(self.gallery_type_ico)
		gallery_text_mode = QWidget()
		grid_gallery_main_l.addRow('Text Mode:', gallery_text_mode)
		gallery_text_mode_l = QHBoxLayout()
		gallery_text_mode.setLayout(gallery_text_mode_l)
		self.gallery_text_elide = QRadioButton('Elide text', gallery_text_mode)
		self.gallery_text_fit = QRadioButton('Fit text', gallery_text_mode)
		gallery_text_mode_l.addWidget(self.gallery_text_elide, 0, Qt.AlignLeft)
		gallery_text_mode_l.addWidget(self.gallery_text_fit, 0, Qt.AlignLeft)
		gallery_text_mode_l.addWidget(Spacer('h'), 1, Qt.AlignLeft)
		gallery_font = QHBoxLayout()
		grid_gallery_main_l.addRow('Font:*', gallery_font)
		self.font_lbl = QLabel()
		self.font_size_lbl = QSpinBox()
		self.font_size_lbl.setMaximum(100)
		self.font_size_lbl.setMinimum(1)
		self.font_size_lbl.setToolTip('Font size in pixels')
		choose_font = QPushButton('Choose font')
		choose_font.clicked.connect(self.choose_font)
		gallery_font.addWidget(self.font_lbl, 0, Qt.AlignLeft)
		gallery_font.addWidget(self.font_size_lbl, 0, Qt.AlignLeft)
		gallery_font.addWidget(choose_font, 0, Qt.AlignLeft)
		gallery_font.addWidget(Spacer('h'), 1, Qt.AlignLeft)
		# grid view / colors
		grid_colors_group = QGroupBox('Colors', grid_view_general_page)
		grid_view_layout.addWidget(grid_colors_group, 1, Qt.AlignTop)
		grid_colors_l = QFormLayout()
		grid_colors_group.setLayout(grid_colors_l)
		def color_lineedit():
			l = QLineEdit()
			l.setPlaceholderText('Hex colors. Eg.: #323232')
			l.setMaximumWidth(200)
			return l
		self.grid_label_color = color_lineedit()
		self.grid_title_color = color_lineedit()
		self.grid_artist_color = color_lineedit()
		grid_colors_l.addRow('Label color:', self.grid_label_color)
		grid_colors_l.addRow('Title color:', self.grid_title_color)
		grid_colors_l.addRow('Artist color:', self.grid_artist_color)

		style_page = QWidget()
		visual.addTab(style_page, 'Style')
		visual.setTabEnabled(0, False)
		visual.setTabEnabled(2, False)
		visual.setCurrentIndex(1)

		# Advanced
		advanced = QTabWidget()
		self.advanced_index = self.right_panel.addWidget(advanced)
		advanced_misc = QWidget()
		advanced.addTab(advanced_misc, 'Misc')
		advanced_misc_main_layout = QVBoxLayout()
		advanced_misc.setLayout(advanced_misc_main_layout)
		misc_controls_layout = QFormLayout()
		misc_controls_layout.addWidget(QLabel('Options marked with * requires application restart'))
		advanced_misc_main_layout.addLayout(misc_controls_layout)
		# Advanced / Misc / Grid View
		misc_gridview = QGroupBox('Grid View')
		misc_controls_layout.addWidget(misc_gridview)
		misc_gridview_layout = QFormLayout()
		misc_gridview.setLayout(misc_gridview_layout)
		# Advanced / Misc / Grid View / scroll speed
		scroll_speed_spin_box = QSpinBox()
		scroll_speed_spin_box.setFixedWidth(60)
		scroll_speed_spin_box.setToolTip('Control the speed when scrolling in'+
								   ' grid view. DEFAULT: 7')
		scroll_speed_spin_box.setValue(self.scroll_speed)
		def scroll_speed(v): self.scroll_speed = v
		scroll_speed_spin_box.valueChanged[int].connect(scroll_speed)
		misc_gridview_layout.addRow('Scroll speed:', scroll_speed_spin_box)
		# Advanced / Misc / Grid View / cache size
		cache_size_spin_box = QSpinBox()
		cache_size_spin_box.setFixedWidth(120)
		cache_size_spin_box.setMaximum(999999999)
		cache_size_spin_box.setToolTip('This will greatly improve the grid view.' +
								 ' Increase the value if you experience lag when scrolling'+
								 ' through galleries. DEFAULT: 200 MiB')
		def cache_size(c): self.cache_size = (self.cache_size[0], c)
		cache_size_spin_box.setValue(self.cache_size[1])
		cache_size_spin_box.valueChanged[int].connect(cache_size)
		misc_gridview_layout.addRow('Cache Size (MiB):', cache_size_spin_box)		
		# Advanced / Misc / Regex
		misc_search = QGroupBox('Search')
		misc_controls_layout.addWidget(misc_search)
		misc_search_layout = QFormLayout()
		misc_search.setLayout(misc_search_layout)
		search_allow_regex_l = QHBoxLayout()
		self.search_allow_regex = QCheckBox()
		self.search_allow_regex.setChecked(gui_constants.ALLOW_SEARCH_REGEX)
		self.search_allow_regex.adjustSize()
		self.search_allow_regex.setToolTip('A regex cheatsheet is located at About->Regex Cheatsheet')
		search_allow_regex_l.addWidget(self.search_allow_regex)
		search_allow_regex_l.addWidget(QLabel('A regex cheatsheet is located at About->Regex Cheatsheet'))
		search_allow_regex_l.addWidget(Spacer('h'))
		misc_search_layout.addRow('Regex:', search_allow_regex_l)
		# Advanced / Misc / Regex / autocomplete
		self.search_autocomplete = QCheckBox('*')
		self.search_autocomplete.setChecked(gui_constants.SEARCH_AUTOCOMPLETE)
		self.search_autocomplete.setToolTip('Turn autocomplete on/off')
		misc_search_layout.addRow('Autocomplete', self.search_autocomplete)
		# Advanced / Misc / Regex / search behaviour
		self.search_every_keystroke = QRadioButton('Search on every keystroke *', misc_search)
		misc_search_layout.addRow(self.search_every_keystroke)
		self.search_on_enter = QRadioButton('Search on enter-key *', misc_search)
		misc_search_layout.addRow(self.search_on_enter)
		# Advanced / Misc / External Viewer
		misc_external_viewer = QGroupBox('External Viewer')
		misc_controls_layout.addWidget(misc_external_viewer)
		misc_external_viewer_l = QFormLayout()
		misc_external_viewer.setLayout(misc_external_viewer_l)
		misc_external_viewer_l.addRow(QLabel(gui_constants.SUPPORTED_EXTERNAL_VIEWER_LBL))
		self.external_viewer_path = PathLineEdit(misc_external_viewer, False)
		self.external_viewer_path.setPlaceholderText('Right/Left-click to open folder explorer.'+
							  ' Leave empty to use default viewer')
		self.external_viewer_path.setToolTip('Right/Left-click to open folder explorer.'+
							  ' Leave empty to use default viewer')
		self.external_viewer_path.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
		misc_external_viewer_l.addRow('Path:', self.external_viewer_path)


		# Advanced / Database
		advanced_db_page = QWidget()
		advanced.addTab(advanced_db_page, 'Database')
		advanced.setTabEnabled(1, False)


		# About
		about = QTabWidget()
		self.about_index = self.right_panel.addWidget(about)
		about_happypanda_page = QWidget()
		about_troubleshoot_page = QWidget()
		about.addTab(about_happypanda_page, 'About Happypanda')
		about_layout = QVBoxLayout()
		about_happypanda_page.setLayout(about_layout)
		info_lbl = QLabel('<b>Author:</b> <a href=\'https://github.com/Pewpews\'>'+
					'Pewpews</a><br/>'+
					'Chat: <a href=\'https://gitter.im/Pewpews/happypanda\'>'+
					'Gitter chat</a><br/>'+
					'Email: [email protected]<br/>'+
					'<b>Current version {}</b><br/>'.format(gui_constants.vs)+
					'Happypanda was created using:<br/>'+
					'- Python 3.4<br/>'+
					'- The Qt5 Framework')
		info_lbl.setOpenExternalLinks(True)
		about_layout.addWidget(info_lbl, 0, Qt.AlignTop)
		gpl_lbl = QLabel(gui_constants.GPL)
		gpl_lbl.setOpenExternalLinks(True)
		gpl_lbl.setWordWrap(True)
		about_layout.addWidget(gpl_lbl, 0, Qt.AlignTop)
		about_layout.addWidget(Spacer('v'))
		# About / Tags
		about_tags_page = QWidget()
		about.addTab(about_tags_page, 'Tags')
		about.setTabEnabled(1, False)
		# list of tags/namespaces here

		# About / Troubleshooting
		about.addTab(about_troubleshoot_page, 'Troubleshooting Guide')
		troubleshoot_layout = QVBoxLayout()
		about_troubleshoot_page.setLayout(troubleshoot_layout)
		guide_lbl = QLabel(gui_constants.TROUBLE_GUIDE)
		guide_lbl.setTextFormat(Qt.RichText)
		guide_lbl.setOpenExternalLinks(True)
		troubleshoot_layout.addWidget(guide_lbl, 0, Qt.AlignTop)
		troubleshoot_layout.addWidget(Spacer('v'))
		# About / Regex Cheatsheet
		about_s_regex = QGroupBox('Regex')
		about.addTab(about_s_regex, 'Regex Cheatsheet')
		about_s_regex_l = QFormLayout()
		about_s_regex.setLayout(about_s_regex_l)
		about_s_regex_l.addRow('\\\\\\\\', QLabel('Match literally \\'))
		about_s_regex_l.addRow('.', QLabel('Match any single character'))
		about_s_regex_l.addRow('^', QLabel('Start of string'))
		about_s_regex_l.addRow('$', QLabel('End of string'))
		about_s_regex_l.addRow('\\d', QLabel('Match any decimal digit'))
		about_s_regex_l.addRow('\\D', QLabel('Match any non-digit character'))
		about_s_regex_l.addRow('\\s', QLabel('Match any whitespace character'))
		about_s_regex_l.addRow('\\S', QLabel('Match any non-whitespace character'))
		about_s_regex_l.addRow('\\w', QLabel('Match any alphanumeric character'))
		about_s_regex_l.addRow('\\W', QLabel('Match any non-alphanumeric character'))
		about_s_regex_l.addRow('*', QLabel('Repeat previous character zero or more times'))
		about_s_regex_l.addRow('+', QLabel('Repeat previous character one or more times'))
		about_s_regex_l.addRow('?', QLabel('Repeat previous character one or zero times'))
		about_s_regex_l.addRow('{m, n}', QLabel('Repeat previous character atleast <i>m</i> times but no more than <i>n</i> times'))
		about_s_regex_l.addRow('(...)', QLabel('Match everything enclosed'))
		about_s_regex_l.addRow('(a|b)', QLabel('Match either a or b'))
		about_s_regex_l.addRow('[abc]', QLabel('Match a single character of: a, b or c'))
		about_s_regex_l.addRow('[^abc]', QLabel('Match a character except: a, b or c'))
		about_s_regex_l.addRow('[a-z]', QLabel('Match a character in the range'))
		about_s_regex_l.addRow('[^a-z]', QLabel('Match a character not in the range'))
		# About / Search tutorial
		about_search_scroll = QScrollArea()
		about_search_scroll.setBackgroundRole(QPalette.Base)
		about_search_scroll.setWidgetResizable(True)
		about_search_tut = QWidget()
		about.addTab(about_search_scroll, 'Search Guide')
		about_search_tut_l = QVBoxLayout()
		about_search_tut.setLayout(about_search_tut_l)
		# General
		about_search_general = QGroupBox('General')
		about_search_tut_l.addWidget(about_search_general)
		about_search_general_l = QFormLayout()
		about_search_general.setLayout(about_search_general_l)
		about_search_general_l.addRow(QLabel(gui_constants.SEARCH_TUTORIAL_GENERAL))
		# Title & Author
		about_search_tit_aut = QGroupBox('Title and Author')
		about_search_tut_l.addWidget(about_search_tit_aut)
		about_search_tit_l = QFormLayout()
		about_search_tit_aut.setLayout(about_search_tit_l)
		about_search_tit_l.addRow(QLabel(gui_constants.SEARCH_TUTORIAL_TIT_AUT))
		# Namespace & Tags
		about_search_tags = QGroupBox('Namespace and Tags')
		about_search_tut_l.addWidget(about_search_tags)
		about_search_tags_l = QFormLayout()
		about_search_tags.setLayout(about_search_tags_l)
		about_search_tags_l.addRow(QLabel(gui_constants.SEARCH_TUTORIAL_TAGS))
		about_search_scroll.setWidget(about_search_tut)

	def add_folder_monitor(self, path=''):
		if not isinstance(path, str):
			path = ''
		l_edit = PathLineEdit()
		l_edit.setText(path)
		n = self.folders_layout.rowCount() + 1
		self.folders_layout.addRow('Dir {}'.format(n), l_edit)

	def color_checker(self, txt):
		allow = False
		if len(txt) == 7:
			if txt[0] == '#':
				allow = True
		return allow

	def choose_font(self):
		tup = QFontDialog.getFont(self)
		font = tup[0]
		if tup[1]:
			self.font_lbl.setText(font.family())
			self.font_size_lbl.setValue(font.pointSize())

	def reject(self):
		self.close()
class _MainContainer(QWidget):

###############################################################################
# MainContainer SIGNALS
###############################################################################
    """
    newFileOpened(QString)
    allTabClosed()
    runFile(QString)
    addToProject(QString)
    showFileInExplorer(QString)
    recentTabsModified()
    currentEditorChanged(QString)
    fileOpened(QString)
    ---------migrationAnalyzed()
    findOcurrences(QString)
    ---------updateFileMetadata()
    editorKeyPressEvent(QEvent)
    locateFunction(QString, QString, bool) [functionName, filePath, isVariable]
    updateLocator(QString)
    beforeFileSaved(QString)
    fileSaved(QString)
    openPreferences()
    --------openProject(QString)
    ---------dontOpenStartPage()
    """
    newFileOpened = pyqtSignal(str)
    allTabClosed = pyqtSignal()
    runFile = pyqtSignal(str)
    addToProject = pyqtSignal(str)
    showFileInExplorer = pyqtSignal(str)
    recentTabsModified = pyqtSignal()
    currentEditorChanged = pyqtSignal(str)
    fileOpened = pyqtSignal(str)
    migrationAnalyzed = pyqtSignal()#-----------
    findOcurrences = pyqtSignal(str)
    updateFileMetadata = pyqtSignal()#-----------
    editorKeyPressEvent = pyqtSignal('QEvent*')
    locateFunction = pyqtSignal(str, str, bool)
    updateLocator = pyqtSignal(str)
    beforeFileSaved = pyqtSignal(str)
    fileSaved = pyqtSignal(str)
    openPreferences = pyqtSignal()
    openProject = pyqtSignal(str)#-----------
    dontOpenStartPage = pyqtSignal()#-----------
    closeDialog = pyqtSignal('QObject*')
    allTabsClosed = pyqtSignal()
    splitEditor = pyqtSignal('QWidget*', 'QWidget*', bool)
    closeSplit = pyqtSignal(QWidget)
    toRemovePreview = pyqtSignal()

###############################################################################

    def __init__(self, parent=None):
        super(_MainContainer, self).__init__(parent)
        self._parent = parent
        self._vbox = QVBoxLayout(self)
        self._vbox.setContentsMargins(0, 0, 0, 0)
        self._vbox.setSpacing(0)
        self.stack = QStackedLayout()
        self.stack.setStackingMode(QStackedLayout.StackAll)
        self._vbox.addLayout(self.stack)

        self.splitter = dynamic_splitter.DynamicSplitter()
        self.setAcceptDrops(True)
        # self._files_handler = files_handler.FilesHandler(self)
        self._add_file_folder = add_file_folder.AddFileFolderWidget(self)

        self.tdir = None

        #documentation browser
        self.docPage = None
        #Code Navigation
        self._locator = locator.GoToDefinition()
        self.__codeBack = []
        self.__codeForward = []
        self.__bookmarksFile = ''
        self.__bookmarksPos = -1
        self.__breakpointsFile = ''
        self.__breakpointsPos = -1
        self.__operations = {
            0: self._navigate_code_jumps,
            1: self._navigate_bookmarks,
            2: self._navigate_breakpoints}

        self.locateFunction.connect(self.locate_function)

        IDE.register_service('main_container', self)

        #Register signals connections
        connections = (
            {'target': 'menu_file',
             'signal_name': 'openFile',#(QString)
             'slot': self.open_file},
            {'target': 'explorer_container',
             'signal_name': 'goToDefinition',#(int)
             'slot': self.editor_go_to_line},
            {'target': 'explorer_container',
             'signal_name': 'pep8Activated',#(bool)
             'slot': self.reset_pep8_warnings},
            {'target': 'explorer_container',
             'signal_name': 'lintActivated',#(bool)
             'slot': self.reset_lint_warnings},
            )

        IDE.register_signals('main_container', connections)

        self.selector = main_selector.MainSelector(self)
        self._opening_dialog = False
        self.add_widget(self.selector)

        if settings.SHOW_START_PAGE:
            self.show_start_page()

        self.selector.changeCurrent[int].connect(self._change_current_stack)
        self.selector.removeWidget[int].connect(self._remove_item_from_stack)
        self.selector.ready.connect(self._selector_ready)
        self.selector.closePreviewer.connect(self._selector_Close)
        self.selector.animationCompleted.connect(self._selector_animation_completed)

        self.closeDialog.connect(self.remove_widget)
        self.stack.widgetRemoved[int].connect(lambda i:print("widgetRemoved._-", i))

    def install(self):
        ide = IDE.getInstance()
        ide.place_me_on("main_container", self, "central", top=True)

        self.combo_area = combo_editor.ComboEditor(original=True)
        self.combo_area.allFilesClosed.connect(self._files_closed)
        self.splitter.add_widget(self.combo_area)
        self.add_widget(self.splitter)

        self.current_widget = self.combo_area

        ui_tools.install_shortcuts(self, actions.ACTIONS, ide)

    def add_status_bar(self, status):
        self._vbox.addWidget(status)

    @property
    def combo_header_size(self):
        return self.combo_area.bar.height()

    def add_widget(self, widget):
        i = self.stack.addWidget(widget)
        #if not isinstance(widget, start_page.StartPage):
        self.tryMakeImagePreview(i)

    def remove_widget(self, widget):
        #self.toRemovePreview.emit(self.stack.widget(widget))
        self.stack.removeWidget(widget)

    def _close_dialog(self, widget):
        self.closeDialog.emit(widget)
        widget.finished[int].disconnect()#lambda i: self._close_dialog(widget))

    def show_dialog(self, widget):
        print("\n\nshow_dialog", self.isVisible())
        self._opening_dialog = True
        widget.finished[int].connect(lambda i: self._close_dialog(widget))
        widget.setVisible(True)
        self.show_selector()

    def show_selector(self):
        print("\n\nshow_selector::", self.selector, self.stack.currentWidget())
        if self.selector != self.stack.currentWidget():
            _dir = self.Successful_Tmp()
            if not _dir:
                print("failed!")
                return

            # temp_dir = os.path.join(QDir.tempPath(), "ninja-ide")
            # if not os.path.exists(temp_dir):
            #     os.mkdir(temp_dir)
            collected_data = []
            current = self.stack.currentIndex()
            for index in range(self.stack.count()):
                widget = self.stack.widget(index)
                if widget == self.selector:
                    continue
                closable = True
                if widget == self.splitter:
                    closable = False
                # path = os.path.join(temp_dir, "screen%s.png" % index)
                #ff = QFile(_dir, "screen%s.png" % index)
                path = _dir.absolutePath()+"/screen%s.png" % index
                pixmap = widget.grab()#widget.rect())
                pixmap.save(path)
                #path = path.replace("\\", '/')
                #print("path::", path, QFileInfo(path).exists())
                path = "file:///"+path
                if index == current:
                    self.selector.set_preview(index, path)#QUrl(path)
                    collected_data.insert(0, (index, path, closable))
                else:
                    collected_data.append((index, path, closable))
            self.selector.set_model(collected_data)
            print("self.selector.set_model()", collected_data)
            self.stack.setCurrentWidget(self.selector)
        else:
            print("\n\n_selector_Close()")
            self._selector_Close()

    def Successful_Tmp(self):# CheckTmpDir, StateTmpDir
        failed = lambda: not self.tdir or not self.tdir.isValid()
        if failed():# not successfully
            self.tdir = QTemporaryDir()
            if failed():
                QMessageBox.critical(self, "Unexpected Failurer", "The application has detected a problem trying to\nCreate or Access a Temporary File!.")
                return None
            else:
                self.tdir.setAutoRemove(True)

        d = QDir(self.tdir.path())
        if not d.exists("ninja-ide"):
            if not d.mkdir("ninja-ide"):
                self.tdir = None
        d.cd("ninja-ide")
        return d

    def tryMakeImagePreview(self, index):
        return
        d = self.Successful_Tmp()
        if d:
            self.makeImagePreview(d, index)

    def makeImagePreview(self, _dir, index):
        return
        path = _dir.absolutePath()+"/screen%s.png" % index
        widget = self.stack.widget(index)
        pixmap = widget.grab()#widget.rect()
        pixmap.save(path)

    def _selector_ready(self):
        self.stack.setCurrentWidget(self.selector)
        self.selector.GoTo_GridPreviews()

    def _selector_Close(self):
        self.stack.setCurrentWidget(self.splitter)

    def _selector_animation_completed(self):
        if self._opening_dialog:
            # We choose the last one with -2, -1 (for last one) +
            # -1 for the hidden selector widget which is in the stacked too.
            self.selector.open_item(self.stack.count() - 2)
        self._opening_dialog = False

    def _change_current_stack(self, index):
        self.stack.setCurrentIndex(index)

    def _remove_item_from_stack(self, index):
        #self.toRemovePreview.emit(index)
        widget = self.stack.takeAt(index)
        del widget

    def show_editor_area(self):
        self.stack.setCurrentWidget(self.splitter)

    def _files_closed(self):
        if settings.SHOW_START_PAGE:
            self.show_start_page()

    def change_visibility(self):
        """Show/Hide the Main Container area."""
        print("change_visibility11")
        if self.isVisible():
            self.hide()
        else:
            self.show()

    def expand_symbol_combo(self):
        self.stack.setCurrentWidget(self.splitter)
        self.current_widget.show_combo_symbol()

    def expand_file_combo(self):
        print("expand_file_combo")
        self.stack.setCurrentWidget(self.splitter)
        self.current_widget.show_combo_file()

    def locate_function(self, function, filePath, isVariable):
        """Move the cursor to the proper position in the navigate stack."""
        editorWidget = self.get_current_editor()
        if editorWidget:
            self.__codeBack.append((editorWidget.file_path,
                                   editorWidget.getCursorPosition()))
            self.__codeForward = []
        self._locator.navigate_to(function, filePath, isVariable)

    def run_file(self, path):
        self.runFile.emit(path)

    def _add_to_project(self, path):
        self.addToProject.emit(path)

    def _show_file_in_explorer(self, path):
        self.showFileInExplorer.emit(path)

    def paste_history(self):
        """Paste the text from the copy/paste history."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            line, index = editorWidget.getCursorPosition()
            central = IDE.get_service('central_container')
            if central:
                editorWidget.insertAt(central.get_paste(), line, index)

    def copy_history(self):
        """Copy the selected text into the copy/paste history."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            copy = editorWidget.selectedText()
            central = IDE.get_service('central_container')
            if central:
                central.add_copy(copy)

    def import_from_everywhere(self):
        """Insert an import line from any place in the editor."""
        editorWidget = self.get_current_editor()
        if editorWidget:
            dialog = from_import_dialog.FromImportDialog(editorWidget, self)
            dialog.show()

    def add_back_item_navigation(self):
        """Add an item to the back stack and reset the forward stack."""
        editorWidget = self.get_current_editor()
        if editorWidget:
            self.__codeBack.append((editorWidget.file_path,
                                   editorWidget.getCursorPosition()))
            self.__codeForward = []

    def preview_in_browser(self):
        """Load the current html file in the default browser."""
        editorWidget = self.get_current_editor()
        if editorWidget:
            if not editorWidget.file_path:
                self.save_file()
            ext = file_manager.get_file_extension(editorWidget.file_path)
            if ext in ('html', 'shpaml', 'handlebars', 'tpl'):
                webbrowser.open_new_tab(editorWidget.file_path)

    def add_bookmark_breakpoint(self):
        """Add a bookmark or breakpoint to the current file in the editor."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            if self.current_widget.bar.code_navigator.operation == 1:
                editorWidget.handle_bookmarks_breakpoints(
                    editorWidget.getCursorPosition()[0], Qt.ControlModifier)
            elif self.current_widget.bar.code_navigator.operation == 2:
                editorWidget.handle_bookmarks_breakpoints(
                    editorWidget.getCursorPosition()[0], Qt.NoModifier)

    def __navigate_with_keyboard(self, val):
        """Navigate between the positions in the jump history stack."""
        op = self.current_widget.bar.code_navigator.operation
        self.navigate_code_history(val, op)

    def navigate_code_history(self, val, op):
        """Navigate the code history."""
        self.__operations[op](val)

    def _navigate_code_jumps(self, val):
        """Navigate between the jump points."""
        node = None
        if not val and self.__codeBack:
            node = self.__codeBack.pop()
            editorWidget = self.get_current_editor()
            if editorWidget:
                self.__codeForward.append((editorWidget.file_path,
                                          editorWidget.getCursorPosition()))
        elif val and self.__codeForward:
            node = self.__codeForward.pop()
            editorWidget = self.get_current_editor()
            if editorWidget:
                self.__codeBack.append((editorWidget.file_path,
                                       editorWidget.getCursorPosition()))
        if node:
            filename = node[0]
            line, col = node[1]
            self.open_file(filename, line, col)

    def _navigate_breakpoints(self, val):
        """Navigate between the breakpoints."""
        #FIXME: put navigate breakpoints and bookmarks as one method.
        breakList = list(settings.BREAKPOINTS.keys())
        breakList.sort()
        if not breakList:
            return
        if self.__breakpointsFile not in breakList:
            self.__breakpointsFile = breakList[0]
        index = breakList.index(self.__breakpointsFile)
        breaks = settings.BREAKPOINTS.get(self.__breakpointsFile, [])
        lineNumber = 0
        #val == True: forward
        if val:
            if (len(breaks) - 1) > self.__breakpointsPos:
                self.__breakpointsPos += 1
                lineNumber = breaks[self.__breakpointsPos]
            elif len(breaks) > 0:
                if index < (len(breakList) - 1):
                    self.__breakpointsFile = breakList[index + 1]
                else:
                    self.__breakpointsFile = breakList[0]
                self.__breakpointsPos = 0
                lineNumber = settings.BREAKPOINTS[self.__breakpointsFile][0]
        else:
            if self.__breakpointsPos > 0:
                self.__breakpointsPos -= 1
                lineNumber = breaks[self.__breakpointsPos]
            elif len(breaks) > 0:
                self.__breakpointsFile = breakList[index - 1]
                breaks = settings.BREAKPOINTS[self.__breakpointsFile]
                self.__breakpointsPos = len(breaks) - 1
                lineNumber = breaks[self.__breakpointsPos]
        if file_manager.file_exists(self.__breakpointsFile):
            self.open_file(self.__breakpointsFile, lineNumber, None, True)
        else:
            settings.BREAKPOINTS.pop(self.__breakpointsFile)
            if settings.BREAKPOINTS:
                self._navigate_breakpoints(val)

    def _navigate_bookmarks(self, val):
        """Navigate between the bookmarks."""
        bookList = list(settings.BOOKMARKS.keys())
        bookList.sort()
        if not bookList:
            return
        if self.__bookmarksFile not in bookList:
            self.__bookmarksFile = bookList[0]
        index = bookList.index(self.__bookmarksFile)
        bookms = settings.BOOKMARKS.get(self.__bookmarksFile, [])
        lineNumber = 0
        #val == True: forward
        if val:
            if (len(bookms) - 1) > self.__bookmarksPos:
                self.__bookmarksPos += 1
                lineNumber = bookms[self.__bookmarksPos]
            elif len(bookms) > 0:
                if index < (len(bookList) - 1):
                    self.__bookmarksFile = bookList[index + 1]
                else:
                    self.__bookmarksFile = bookList[0]
                self.__bookmarksPos = 0
                lineNumber = settings.BOOKMARKS[self.__bookmarksFile][0]
        else:
            if self.__bookmarksPos > 0:
                self.__bookmarksPos -= 1
                lineNumber = bookms[self.__bookmarksPos]
            elif len(bookms) > 0:
                self.__bookmarksFile = bookList[index - 1]
                bookms = settings.BOOKMARKS[self.__bookmarksFile]
                self.__bookmarksPos = len(bookms) - 1
                lineNumber = bookms[self.__bookmarksPos]
        if file_manager.file_exists(self.__bookmarksFile):
            self.open_file(self.__bookmarksFile,
                           lineNumber, None, True)
        else:
            settings.BOOKMARKS.pop(self.__bookmarksFile)
            if settings.BOOKMARKS:
                self._navigate_bookmarks(val)

    def count_file_code_lines(self):
        """Count the lines of code in the current file."""
        editorWidget = self.get_current_editor()
        if editorWidget:
            block_count = editorWidget.lines()
            blanks = re.findall('(^\n)|(^(\s+)?#)|(^( +)?($|\n))',
                                editorWidget.text(), re.M)
            blanks_count = len(blanks)
            resume = self.tr("Lines code: %s\n") % (block_count - blanks_count)
            resume += (self.tr("Blanks and commented lines: %s\n\n") %
                       blanks_count)
            resume += self.tr("Total lines: %s") % block_count
            msgBox = QMessageBox(QMessageBox.Information,
                                 self.tr("Summary of lines"), resume,
                                 QMessageBox.Ok, editorWidget)
            msgBox.exec_()

    def editor_cut(self):
        editorWidget = self.get_current_editor()
        if editorWidget:
            editorWidget.cut()

    def editor_copy(self):
        editorWidget = self.get_current_editor()
        if editorWidget:
            editorWidget.copy()

    def editor_paste(self):
        editorWidget = self.get_current_editor()
        if editorWidget:
            editorWidget.paste()

    def editor_upper(self):
        editorWidget = self.get_current_editor()
        if editorWidget:
            editorWidget.to_upper()

    def editor_lower(self):
        editorWidget = self.get_current_editor()
        if editorWidget:
            editorWidget.to_lower()

    def editor_title(self):
        editorWidget = self.get_current_editor()
        if editorWidget:
            editorWidget.to_title()

    def editor_go_to_definition(self):
        """Search the definition of the method or variable under the cursor.

        If more than one method or variable is found with the same name,
        shows a table with the results and let the user decide where to go."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            editorWidget.go_to_definition()

    def editor_redo(self):
        """Execute the redo action in the current editor."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            editorWidget.redo()

    def editor_undo(self):
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            editorWidget.undo()

    def editor_indent_less(self):
        """Indent 1 position to the left for the current line or selection."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            editorWidget.indent_less()

    def editor_indent_more(self):
        """Indent 1 position to the right for the current line or selection."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            editorWidget.indent_more()

    def editor_insert_debugging_prints(self):
        """Insert a print statement in each selected line."""
        editorWidget = self.get_current_editor()
        if editorWidget:
            helpers.insert_debugging_prints(editorWidget)

    def editor_insert_pdb(self):
        """Insert a pdb.set_trace() statement in tjhe current line."""
        editorWidget = self.get_current_editor()
        if editorWidget:
            helpers.insert_pdb(editorWidget)

    def editor_comment(self):
        """Mark the current line or selection as a comment."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            helpers.comment(editorWidget)

    def editor_uncomment(self):
        """Uncomment the current line or selection."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            helpers.uncomment(editorWidget)

    def editor_insert_horizontal_line(self):
        """Insert an horizontal lines of comment symbols."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            helpers.insert_horizontal_line(editorWidget)

    def editor_insert_title_comment(self):
        """Insert a Title surrounded by comment symbols."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            helpers.insert_title_comment(editorWidget)

    def editor_remove_trailing_spaces(self):
        """Remove the trailing spaces in the current editor."""
        editorWidget = self.get_current_editor()
        if editorWidget:
            helpers.remove_trailing_spaces(editorWidget)

    def editor_replace_tabs_with_spaces(self):
        """Replace the Tabs with Spaces in the current editor."""
        editorWidget = self.get_current_editor()
        if editorWidget:
            helpers.replace_tabs_with_spaces(editorWidget)

    def editor_move_up(self):
        """Move the current line or selection one position up."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            helpers.move_up(editorWidget)

    def editor_move_down(self):
        """Move the current line or selection one position down."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            helpers.move_down(editorWidget)

    def editor_remove_line(self):
        """Remove the current line or selection."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            helpers.remove_line(editorWidget)

    def editor_duplicate(self):
        """Duplicate the current line or selection."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            helpers.duplicate(editorWidget)

    def editor_highlight_word(self):
        """Highlight the occurrences of the current word in the editor."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            editorWidget.highlight_selected_word()

    def editor_complete_declaration(self):
        """Do the opposite action that Complete Declaration expect."""
        editorWidget = self.get_current_editor()
        if editorWidget and editorWidget.hasFocus():
            editorWidget.complete_declaration()

    def editor_go_to_line(self, line, select=False):#def editor_go_to_line(self, line):
        """Jump to the specified line in the current editor."""
        editorWidget = self.get_current_editor()
        print("\nluego en segundo lugar por aca")
        if editorWidget:
            editorWidget.jump_to_line(line)#select

    def editor_go_to_symbol_line(self, line, sym= "", select=False):
        """Jump to the specified line in the current editor."""
        editorWidget = self.get_current_editor()
        print("\nluego en segundo lugar por aca")
        if editorWidget:
            editorWidget.go_to_symbol(line, sym, select)#select

    def zoom_in_editor(self):
        """Increase the font size in the current editor."""
        editorWidget = self.get_current_editor()
        if editorWidget:
            editorWidget.zoom_in()

    def zoom_out_editor(self):
        """Decrease the font size in the current editor."""
        editorWidget = self.get_current_editor()
        if editorWidget:
            editorWidget.zoom_out()

    def recent_files_changed(self):
        self.recentTabsModified.emit()

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

    def dropEvent(self, event):
        # file_path = event.mimeData().urls()[0].toLocalFile()
        # paths = [item.toLocalFile() for item in event.mimeData().urls()]
        self.open_files_fromUrlList(event.mimeData().urls())
        # print("\n\n dropEvent", paths)
        # self.open_file(file_path)

    def setFocus(self):
        widget = self.get_current_widget()
        if widget:
            widget.setFocus()

    def current_editor_changed(self, filename):
        """Notify the new filename of the current editor."""
        if filename is None:
            filename = translations.TR_NEW_DOCUMENT
        self.currentEditorChanged.emit(filename)

    def show_split(self, orientation_vertical=False):
        #IDE.select_current(self.current_widget.currentWidget())
        self.current_widget.split_editor(orientation_vertical)

    def add_editor(self, fileName=None, ignore_checkers=False):
        print("filename::", fileName)
        ninjaide = IDE.getInstance()
        editable = ninjaide.get_or_create_editable(fileName)
        if editable.editor:
            self.current_widget.set_current(editable)
            print("\n\nreturn")
            return self.current_widget.currentWidget()
        else:
            editable.ignore_checkers = ignore_checkers

        editorWidget = self.create_editor_from_editable(editable)

        #add the tab
        keep_index = (self.splitter.count() > 1 and
                      self.combo_area.stacked.count() > 0)
        self.combo_area.add_editor(editable, keep_index)

        #emit a signal about the file open
        self.fileOpened.emit(fileName)
        if keep_index:
            self.current_widget.set_current(editable)

        self.stack.setCurrentWidget(self.splitter)
        return editorWidget

    def create_editor_from_editable(self, editable):
        editorWidget = editor.create_editor(editable)

        #Connect signals
        editable.fileSaved.connect(self._editor_tab_was_saved)
        editorWidget.openDropFile.connect(self.open_file)
        editorWidget.addBackItemNavigation.connect(self.add_back_item_navigation)
        editorWidget.locateFunction.connect(self._editor_locate_function)
        editorWidget.findOcurrences.connect(self._find_occurrences)
        #keyPressEventSignal for plugins
        editorWidget.keyPressSignal.connect(self._editor_keyPressEvent)

        return editorWidget

    def reset_pep8_warnings(self, value):
        pass
        #FIXME: check how we handle this
        #for i in range(self._tabMain.count()):
            #widget = self._tabMain.widget(i)
            #if type(widget) is editor.Editor:
                #if value:
                    #widget.syncDocErrorsSignal = True
                    #widget.pep8.check_style()
                #else:
                    #widget.hide_pep8_errors()

    def reset_lint_warnings(self, value):
        pass
        #FIXME: check how we handle this
        #for i in range(self._tabMain.count()):
            #widget = self._tabMain.widget(i)
            #if type(widget) is editor.Editor:
                #if value:
                    #widget.syncDocErrorsSignal = True
                    #widget.errors.check_errors()
                #else:
                    #widget.hide_lint_errors()

    def _find_occurrences(self, word):
        self.findOcurrences.emit(word)

    def _editor_keyPressEvent(self, event):
        self.editorKeyPressEvent.emit(event)

    def _editor_locate_function(self, function, filePath, isVariable):
        self.locateFunction.emit(function, filePath, isVariable)

    def _editor_tab_was_saved(self, editable=None):
        self.updateLocator.emit(editable.file_path)

    def get_current_widget(self):
        return self.current_widget.currentWidget()

    def get_current_editor(self):
        """Return the Actual Editor or None

        Return an instance of Editor if the Current Tab contains
        an Editor or None if it is not an instance of Editor"""
        widget = self.current_widget.currentWidget()
        if isinstance(widget, editor.Editor):
            return widget
        return None

    def reload_file(self, editorWidget=None):
        if editorWidget is None:
            editorWidget = self.get_current_editor()
            if editorWidget is not None:
                editorWidget.neditable.reload_file()

    def add_tab(self, widget, tabName, tabIndex=None):
        pass
        #return self.tabs.add_tab(widget, tabName, index=tabIndex)

    def open_image(self, fileName):
        try:
            if not self.is_open(fileName):
                viewer = image_viewer.ImageViewer(fileName)
                self.add_tab(viewer, file_manager.get_basename(fileName))
                viewer.ID = fileName
            else:
                self.move_to_open(fileName)
        except Exception as reason:
            logger.error('open_image: %s', reason)
            QMessageBox.information(self, self.tr("Incorrect File"),
                                    self.tr("The image couldn\'t be open"))

    def open_files_fromList(self, lst):
        for f in lst:
            self.open_file(f)

    def open_files_fromUrlList(self, lst):
        for f in lst:
            self.open_file(f.toLocalFile())

    def open_file(self, filename='', line=-1, col=0, ignore_checkers=False):
        logger.debug("will try to open %s" % filename)
        if not filename:
            logger.debug("has nofilename")
            if settings.WORKSPACE:
                directory = settings.WORKSPACE
            else:
                directory = os.path.expanduser("~")
                editorWidget = self.get_current_editor()
                ninjaide = IDE.getInstance()
                if ninjaide:
                    current_project = ninjaide.get_current_project()
                    if current_project is not None:
                        directory = current_project
                    elif editorWidget is not None and editorWidget.file_path:
                        directory = file_manager.get_folder(
                            editorWidget.file_path)
            extensions = ';;'.join(
                ['{}(*{})'.format(e.upper()[1:], e)
                 for e in settings.SUPPORTED_EXTENSIONS + ['.*', '']])
            fileNames = QFileDialog.getOpenFileNames(self,
                             self.tr("Open File"), directory, extensions)[0]#list()
        else:
            logger.debug("has filename")
            fileNames = [filename]
        if not fileNames:
            return

        print("\n\nopen_file")
        othersFileNames = []
        image_extensions = ('bmp', 'gif', 'jpeg', 'jpg', 'png')
        for filename in fileNames:
            print("nombre", filename)
            if QFileInfo(filename).isDir():
                othersFileNames.extend( QFileDialog.getOpenFileNames(None,
                    "Select files", filename, "Files (*.*)")[0] )
            elif file_manager.get_file_extension(filename) in image_extensions:
                logger.debug("will open as image")
                self.open_image(filename)
            elif file_manager.get_file_extension(filename).endswith('ui'):
                logger.debug("will load in ui editor")
                self.w = uic.loadUi(filename)
                self.w.show()
            else:
                logger.debug("will try to open: " + filename)
                self.__open_file(filename, line, col,
                                 ignore_checkers)

        for filename in othersFileNames:
            print("nombre", filename)
            if QFileInfo(filename).isDir():
                continue
            elif file_manager.get_file_extension(filename) in image_extensions:
                logger.debug("will open as image")
                self.open_image(filename)
            elif file_manager.get_file_extension(filename).endswith('ui'):
                logger.debug("will load in ui editor")
                self.w = uic.loadUi(filename)
                self.w.show()
            else:
                logger.debug("will try to open: " + filename)
                self.__open_file(filename, line, col,
                                 ignore_checkers)



    def __open_file(self, fileName='', line=-1, col=0, ignore_checkers=False):
        print("unio", fileName)
        try:
            editorWidget = self.add_editor(fileName,
                                           ignore_checkers=ignore_checkers)
            if line != -1:
                editorWidget.set_cursor_position(line, col)
            self.currentEditorChanged.emit(fileName)
        except file_manager.NinjaIOException as reason:
            QMessageBox.information(self,
                                    self.tr("The file couldn't be open"),
                                    str(reason))

    def is_open(self, filename):
        pass
        #return self.tabs.is_open(filename) != -1

    def move_to_open(self, filename):
        pass
        #FIXME: add in the current split?
        #if self.tabs.is_open(filename) != -1:
            #self.tabs.move_to_open(filename)
        #self.tabs.currentWidget().setFocus()
        #self.emit(SIGNAL("currentEditorChanged(QString)"), filename)

    def get_widget_for_id(self, filename):
        pass
        #widget = None
        #index = self.tabs.is_open(filename)
        #if index != -1:
            #widget = self.tabs.widget(index)
        #return widget

    def change_open_tab_id(self, idname, newId):
        """Search for the Tab with idname, and set the newId to that Tab."""
        pass
        #index = self.tabs.is_open(idname)
        #if index != -1:
            #widget = self.tabs.widget(index)
            #tabName = file_manager.get_basename(newId)
            #self.tabs.change_open_tab_name(index, tabName)
            #widget.ID = newId

    def close_deleted_file(self, idname):
        """Search for the Tab with id, and ask the user if should be closed."""
        pass
        #index = self.tabs.is_open(idname)
        #if index != -1:
            #result = QMessageBox.question(self, self.tr("Close Deleted File"),
                #self.tr("Are you sure you want to close the deleted file?\n"
                        #"The content will be completely deleted."),
                #buttons=QMessageBox.Yes | QMessageBox.No)
            #if result == QMessageBox.Yes:
                #self.tabs.removeTab(index)

    def save_file(self, editorWidget=None):
        #FIXME: check how we handle this
        if not editorWidget:
            editorWidget = self.get_current_editor()
        if not editorWidget:
            return False
        try:
            #editorWidget.just_saved = True
            if (editorWidget.nfile.is_new_file or
                    not editorWidget.nfile.has_write_permission()):
                return self.save_file_as()

            self.beforeFileSaved.emit(editorWidget.file_path)
            if settings.REMOVE_TRAILING_SPACES:
                helpers.remove_trailing_spaces(editorWidget)
            editorWidget.neditable.save_content()
            #file_manager.store_file_content(
                #fileName, content, addExtension=False)
            encoding = file_manager.get_file_encoding(editorWidget.text())
            editorWidget.encoding = encoding
            self.fileSaved.emit((self.tr("File Saved: %s") % editorWidget.file_path))
            return True
        except Exception as reason:
            logger.error('save_file: %s', reason)
            QMessageBox.information(self, self.tr("Save Error"),
                                    self.tr("The file couldn't be saved!"))
        return False

    def save_file_as(self):
        editorWidget = self.get_current_editor()
        if not editorWidget:
            return False
        try:
            filters = '(*.py);;(*.*)'
            if editorWidget.file_path:
                ext = file_manager.get_file_extension(editorWidget.file_path)
                if ext != 'py':
                    filters = '(*.%s);;(*.py);;(*.*)' % ext
            save_folder = self._get_save_folder(editorWidget.file_path)
            fileName = QFileDialog.getSaveFileName(
                self._parent, self.tr("Save File"), save_folder, filters)
            if not fileName:
                return False

            if settings.REMOVE_TRAILING_SPACES:
                helpers.remove_trailing_spaces(editorWidget)

            editorWidget.neditable.save_content(path=fileName)
            editorWidget.register_syntax(
                file_manager.get_file_extension(fileName))

            self.fileSaved.emit((self.tr("File Saved: %s") % fileName))
            self.currentEditorChanged.emit(fileName)
            return True
        except file_manager.NinjaFileExistsException as ex:
            QMessageBox.information(self, self.tr("File Already Exists"),
                                    (self.tr("Invalid Path: the file '%s' "
                                             " already exists.") %
                                    ex.filename))
        except Exception as reason:
            logger.error('save_file_as: %s', reason)
            QMessageBox.information(self, self.tr("Save Error"),
                                    self.tr("The file couldn't be saved!"))
        return False

    def _get_save_folder(self, fileName):
        """
        Returns the root directory of the 'Main Project' or the home folder
        """
        ninjaide = IDE.getInstance()
        current_project = ninjaide.get_current_project()
        if current_project:
            return current_project.path
        return os.path.expanduser("~")

    def save_project(self, projectFolder):
        pass
        #FIXME: check how we handle this
        #for i in range(self._tabMain.count()):
            #editorWidget = self._tabMain.widget(i)
            #if type(editorWidget) is editor.Editor and \
            #file_manager.belongs_to_folder(projectFolder,
                    #editorWidget.file_path):
                #reloaded = self._tabMain.check_for_external_modifications(
                    #editorWidget)
                #if not reloaded:
                    #self.save_file(editorWidget)
        #for i in range(self.tabsecondary.count()):
            #editorWidget = self.tabsecondary.widget(i)
            #if type(editorWidget) is editor.Editor and \
            #file_manager.belongs_to_folder(projectFolder,
                    #editorWidget.file_path):
                #reloaded = self.tabsecondary.check_for_external_modifications(
                    #editorWidget)
                #if not reloaded:
                    #self.save_file(editorWidget)

    def save_all(self):
        pass
        #FIXME: check how we handle this
        #for i in range(self._tabMain.count()):
            #editorWidget = self._tabMain.widget(i)
            #if type(editorWidget) is editor.Editor:
                #reloaded = self._tabMain.check_for_external_modifications(
                    #editorWidget)
                #if not reloaded:
                    #self.save_file(editorWidget)
        #for i in range(self.tabsecondary.count()):
            #editorWidget = self.tabsecondary.widget(i)
            #self.tabsecondary.check_for_external_modifications(editorWidget)
            #if type(editorWidget) is editor.Editor:
                #reloaded = self.tabsecondary.check_for_external_modifications(
                    #editorWidget)
                #if not reloaded:
                    #self.save_file(editorWidget)

    def call_editors_function(self, call_function, *arguments):
        pass
        #args = arguments[0]
        #kwargs = arguments[1]
        #for i in range(self.tabs.count()):
            #editorWidget = self.tabs.widget(i)
            #if isinstance(editorWidget, editor.Editor):
                #function = getattr(editorWidget, call_function)
                #function(*args, **kwargs)
        #TODO: add other splits

    def show_start_page(self):
        start = self.stack.widget(0)
        if isinstance(start, start_page.StartPage):
            self.stack.setCurrentIndex(0)
        else:
            startPage = start_page.StartPage(parent=self)
            startPage.openProject.connect(self.open_project)
            startPage.openPreferences.connect(self.openPreferences.emit)
            startPage.newFile.connect(self.add_editor)
            startPage.openFiles.connect(self.open_files_fromList)
            self.stack.insertWidget(0, startPage)
            self.stack.setCurrentIndex(0)

            self.tryMakeImagePreview(0)
            #"screen0.png"

    def show_python_doc(self):
        if sys.platform == 'win32':
            self.docPage = browser_widget.BrowserWidget(
                'http://docs.python.org/')
        else:
            process = runner.start_pydoc()
            self.docPage = browser_widget.BrowserWidget(process[1], process[0])
        self.add_tab(self.docPage, translations.TR_PYTHON_DOC)

    def show_report_bugs(self):
        webbrowser.open(resources.BUGS_PAGE)

    def show_plugins_doc(self):
        bugsPage = browser_widget.BrowserWidget(resources.PLUGINS_DOC, self)
        self.add_tab(bugsPage, translations.TR_HOW_TO_WRITE_PLUGINS)

    def editor_jump_to_line(self, lineno=None):
        """Jump to line *lineno* if it is not None
        otherwise ask to the user the line number to jump
        """
        editorWidget = self.get_current_editor()
        if editorWidget:
            editorWidget.jump_to_line(lineno=lineno)

    def get_opened_documents(self):
        #return self.tabs.get_documents_data()
        return []

    def check_for_unsaved_files(self):
        pass
        #return self.tabs._check_unsaved_tabs()

    def get_unsaved_files(self):
        pass
        #return self.tabs.get_unsaved_files()

    def reset_editor_flags(self):
        pass
        #for i in range(self.tabs.count()):
            #widget = self.tabs.widget(i)
            #if isinstance(widget, editor.Editor):
                #widget.set_flags()

    def _specify_syntax(self, widget, syntaxLang):
        if isinstance(widget, editor.Editor):
            widget.restyle(syntaxLang)

    def apply_editor_theme(self, family, size):
        pass
        #for i in range(self.tabs.count()):
            #widget = self.tabs.widget(i)
            #if isinstance(widget, editor.Editor):
                #widget.restyle()
                #widget.set_font(family, size)

    def update_editor_margin_line(self):
        pass
        #for i in range(self.tabs.count()):
            #widget = self.tabs.widget(i)
            #if isinstance(widget, editor.Editor):
                #widget._update_margin_line()

    def open_project(self, path):
        print("open_project")
        self.openProject.emit(path)

    def close_python_doc(self):
        pass
        #close the python document server (if running)
        #if self.docPage:
            #index = self.tabs.indexOf(self.docPage)
            #self.tabs.removeTab(index)
            ##assign None to the browser
            #self.docPage = None

    def close_file(self):
        """Close the current tab in the current TabWidget."""
        self.current_widget.close_current_file()

    def create_file(self, base_path, project_path):
        self._add_file_folder.create_file(base_path, project_path)

    def create_folder(self, base_path, project_path):
        self._add_file_folder.create_folder(base_path, project_path)

    def change_tab(self):
        """Change the tab in the current TabWidget."""
        print("\nchange_tab")
        self.stack.setCurrentWidget(self.splitter)
        # self._files_handler.next_item()
        pass

    def change_tab_reverse(self):
        """Change the tab in the current TabWidget backwards."""
        print("\nchange_tab_reverse")
        self.stack.setCurrentWidget(self.splitter)
        # self._files_handler.previous_item()

    def toggle_tabs_and_spaces(self):
        """ Toggle Show/Hide Tabs and Spaces """

        settings.SHOW_TABS_AND_SPACES = not settings.SHOW_TABS_AND_SPACES
        qsettings = IDE.ninja_settings()
        qsettings.setValue('preferences/editor/showTabsAndSpaces',
                           settings.SHOW_TABS_AND_SPACES)

    def show_navigation_buttons(self):
        """Show Navigation menu."""
        self.stack.setCurrentWidget(self.splitter)
        self.combo_area.show_menu_navigation()

    def change_split_focus(self):
        pass
        #FIXME: check how we handle this
        #if self.actualTab == self._tabMain and self.tabsecondary.isVisible():
            #self.actualTab = self.tabsecondary
        #else:
            #self.actualTab = self._tabMain
        #widget = self.actualTab.currentWidget()
        #if widget is not None:
            #widget.setFocus()

    def shortcut_index(self, index):
        pass
        #self.tabs.setCurrentIndex(index)

    def print_file(self):
        """Call the print of ui_tool

        Call print of ui_tool depending on the focus of the application"""
        #TODO: Add funtionality for proyect tab and methods tab
        editorWidget = self.get_current_editor()
        if editorWidget is not None:
            fileName = "newDocument.pdf"
            if editorWidget.file_path:
                fileName = file_manager.get_basename(
                    editorWidget.file_path)
                fileName = fileName[:fileName.rfind('.')] + '.pdf'
            ui_tools.print_file(fileName, editorWidget.print_)

    def split_assistance(self):
        dialog = split_orientation.SplitOrientation(self)
        dialog.show()

    def close_split(self):
        if self.current_widget != self.combo_area:
            self.current_widget.bar.close_split()

    def split_vertically(self):
        self.show_split(False)

    def split_horizontally(self):
        self.show_split(True)

    def navigate_back(self):
        self.__navigate_with_keyboard(False)

    def navigate_forward(self):
        self.__navigate_with_keyboard(True)
Esempio n. 40
0
class ComboEditor(QWidget):
    # Signals
    closeSplit = pyqtSignal('PyQt_PyObject')
    splitEditor = pyqtSignal(
        'PyQt_PyObject', 'PyQt_PyObject', Qt.Orientation)
    allFilesClosed = pyqtSignal()
    about_to_close_combo_editor = pyqtSignal()
    fileClosed = pyqtSignal("PyQt_PyObject")

    def __init__(self, original=False):
        super(ComboEditor, self).__init__(None)
        self.__original = original
        self.__undocked = []
        self._symbols_index = []
        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)

        self.bar = ActionBar(main_combo=original)
        vbox.addWidget(self.bar)

        # Info bar
        # self.info_bar = InfoBar(self)
        # self.info_bar.setVisible(False)
        # vbox.addWidget(self.info_bar)

        self.stacked = QStackedLayout()
        vbox.addLayout(self.stacked)

        self._main_container = IDE.get_service('main_container')

        if not self.__original:
            self._main_container.fileOpened['QString'].connect(
                self._file_opened_by_main)

        self.bar.combo_files.showComboSelector.connect(
            self._main_container.show_files_handler)
        self.bar.combo_files.hideComboSelector.connect(
            self._main_container.hide_files_handler)
        self.bar.change_current['PyQt_PyObject',
                                int].connect(self._set_current)
        self.bar.splitEditor[bool].connect(self.split_editor)
        self.bar.runFile['QString'].connect(self._run_file)
        self.bar.closeSplit.connect(lambda: self.closeSplit.emit(self))
        self.bar.addToProject['QString'].connect(self._add_to_project)
        self.bar.showFileInExplorer['QString'].connect(
            self._show_file_in_explorer)
        self.bar.goToSymbol[int].connect(self._go_to_symbol)
        self.bar.undockEditor.connect(self.undock_editor)
        self.bar.reopenTab['QString'].connect(
            lambda path: self._main_container.open_file(path))
        self.bar.closeImageViewer.connect(self._close_image)
        self.bar.code_navigator.previousPressed.connect(self._navigate_code)
        self.bar.code_navigator.nextPressed.connect(self._navigate_code)
        # self.connect(self.bar, SIGNAL("recentTabsModified()"),
        #             lambda: self._main_container.recent_files_changed())
        # self.connect(self.bar.code_navigator.btnPrevious,
        #                SIGNAL("clicked()"),
        #             lambda: self._navigate_code(False))
        # self.connect(self.bar.code_navigator.btnNext, SIGNAL("clicked()"),
        #             lambda: self._navigate_code(True))

    def _navigate_code(self, operation, forward=True):
        self._main_container.navigate_code_history(operation, forward)
    #    op = self.bar.code_navigator.operation
    #    self._main_container.navigate_code_history(val, op)

    def current_editor(self):
        return self.stacked.currentWidget()

    def setFocus(self):
        super(ComboEditor, self).setFocus()
        self.current_editor().setFocus()
        self._editor_with_focus()

    def _file_opened_by_main(self, path):
        index = self.stacked.currentIndex()
        ninjaide = IDE.get_service('ide')
        editable = ninjaide.get_or_create_editable(path)
        self.add_editor(editable)
        self.bar.set_current_by_index(index)
        if index == -1:
            self.bar.set_current_by_index(0)

    def add_image_viewer(self, viewer):
        """Add Image Viewer widget to the UI area"""

        self.stacked.addWidget(viewer)
        viewer.scaleFactorChanged.connect(
            self.bar.image_viewer_controls.update_scale_label)
        viewer.imageSizeChanged.connect(
            self.bar.image_viewer_controls.update_size_label)
        self.bar.add_item(viewer.display_name(), None)
        viewer.create_scene()
        if not self.bar.isVisible():
            self.bar.setVisible(True)

    def add_editor(self, neditable, keep_index=False):
        """Add Editor Widget to the UI area."""
        if neditable.editor:
            if self.__original:
                editor = neditable.editor
            else:
                # editor = neditable.editor.clone()
                editor = self._main_container.create_editor_from_editable(
                   neditable)
                neditable.editor.link(editor)

            current_index = self.stacked.currentIndex()
            new_index = self.stacked.addWidget(editor)
            self.stacked.setCurrentIndex(new_index)
            self.bar.add_item(neditable.display_name, neditable)
            # Bar is not visible because all the files have been closed,
            # so if a new file is opened, show the bar
            if not self.bar.isVisible():
                self.bar.setVisible(True)
            if keep_index:
                self.bar.set_current_by_index(current_index)

            # Connections
            neditable.fileClosing.connect(self._close_file)
            neditable.fileSaved.connect(self._update_symbols)
            editor.editorFocusObtained.connect(self._editor_with_focus)
            editor.modificationChanged.connect(self._editor_modified)
            editor.cursor_position_changed[int, int].connect(
                self._update_cursor_position)
            editor.current_line_changed[int].connect(self._set_current_symbol)
            if neditable._swap_file.dirty:
                self._editor_modified(True, sender=editor)
            neditable.checkersUpdated.connect(self._show_notification_icon)
            # Connect file system signals only in the original
            if self.__original:
                neditable.askForSaveFileClosing.connect(self._ask_for_save)
                neditable.fileChanged.connect(self._file_has_been_modified)
            # Load Symbols
            self._load_symbols(neditable)

    def show_combo_file(self):
        self.bar.combo.showPopup()

    def show_combo_symbol(self):
        self.bar.symbols_combo.showPopup()

    def show_combo_set_language(self):
        self.bar.set_language_combo.showPopup()

    def unlink_editors(self):
        for index in range(self.stacked.count()):
            widget = self.stacked.widget(index)
            # widget.setDocument(QsciDocument())

    def clone(self):
        combo = ComboEditor()
        for neditable in self.bar.get_editables():
            combo.add_editor(neditable)
        return combo

    def split_editor(self, orientation):
        new_combo = self.clone()
        self.splitEditor.emit(self, new_combo, orientation)

    def undock_editor(self):
        new_combo = ComboEditor()
        for neditable in self.bar.get_editables():
            new_combo.add_editor(neditable)
        self.__undocked.append(new_combo)
        new_combo.setWindowTitle("NINJA-IDE")
        editor = self.current_editor()
        new_combo.set_current(editor.neditable)
        new_combo.resize(700, 500)
        new_combo.about_to_close_combo_editor.connect(self._remove_undock)
        new_combo.show()

    def _remove_undock(self):
        widget = self.sender()
        self.__undocked.remove(widget)

    def close_current_file(self):
        self.bar.about_to_close_file()

    def _close_image(self, index):
        layout_item = self.stacked.takeAt(index)
        layout_item.widget().deleteLater()
        if self.stacked.isEmpty():
            self.bar.hide()
            self.allFilesClosed.emit()

    def _close_file(self, neditable):
        index = self.bar.close_file(neditable)
        layoutItem = self.stacked.takeAt(index)
        # neditable.editor.completer.cc.unload_module()
        self.fileClosed.emit(neditable.nfile)
        layoutItem.widget().deleteLater()

        if self.stacked.isEmpty():
            self.bar.hide()
            self.allFilesClosed.emit()
            tree_symbols = IDE.get_service("symbols_explorer")
            if tree_symbols is not None:
                tree_symbols.clear()

    def _editor_with_focus(self):
        self._main_container.combo_area = self
        editor = self.current_editor()
        if editor is not None:
            self._main_container.current_editor_changed(
                editor.neditable.file_path)
            self._load_symbols(editor.neditable)
            editor.neditable.update_checkers_display()

    def _ask_for_save(self, neditable):
        val = QMessageBox.No
        fileName = neditable.nfile.file_name
        val = QMessageBox.question(
            self, (self.tr('The file %s was not saved') %
                   fileName),
            self.tr("Do you want to save before closing?"),
            QMessageBox.Yes | QMessageBox.No |
            QMessageBox.Cancel)
        if val == QMessageBox.No:
            neditable.nfile.close(force_close=True)
        elif val == QMessageBox.Yes:
            neditable.ignore_checkers = True
            self._main_container.save_file(neditable.editor)
            neditable.nfile.close()

    @pyqtSlot("PyQt_PyObject")
    def _recovery(self, neditable):
        print("lalalal")

    def _file_has_been_modified(self, neditable):
        index = self.bar.combo_files.findData(neditable)
        self.stacked.setCurrentIndex(index)
        self.bar.combo_files.setCurrentIndex(index)

        msg_box = QMessageBox(self)
        msg_box.setIcon(QMessageBox.Information)
        msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        msg_box.setDefaultButton(QMessageBox.Yes)
        msg_box.setWindowTitle(translations.TR_FILE_HAS_BEEN_MODIFIED)
        msg_box.setText(
            translations.TR_FILE_MODIFIED_OUTSIDE % neditable.display_name)

        result = msg_box.exec_()
        if result == QMessageBox.Yes:
            neditable.reload_file()
        return

    def _run_file(self, path):
        self._main_container.run_file(path)

    def _add_to_project(self, path):
        self._main_container._add_to_project(path)

    def _show_file_in_explorer(self, path):
        '''Connected to ActionBar's showFileInExplorer(QString)
        signal, forwards the file path on to the main container.'''

        self._main_container._show_file_in_explorer(path)

    def set_current(self, neditable):
        if neditable:
            self.bar.set_current_file(neditable)

    def _set_current(self, neditable, index):
        self.stacked.setCurrentIndex(index)
        if neditable:
            self.bar.image_viewer_controls.setVisible(False)
            self.bar.code_navigator.setVisible(True)
            self.bar.symbols_combo.setVisible(True)
            self.bar.lbl_position.setVisible(True)

            editor = self.current_editor()
            self._update_cursor_position(ignore_sender=True)
            editor.setFocus()
            self._main_container.current_editor_changed(
                neditable.file_path)
            self._load_symbols(neditable)
            # self._show_file_in_explorer(neditable.file_path)
            neditable.update_checkers_display()
        else:
            self.bar.combo_files.setCurrentIndex(index)
            viewer_widget = self.stacked.widget(index)
            self._main_container.current_editor_changed(
                viewer_widget.image_filename)
            self.bar.image_viewer_controls.setVisible(True)
            self.bar.code_navigator.setVisible(False)
            self.bar.symbols_combo.setVisible(False)
            self.bar.lbl_position.setVisible(False)

    def widget(self, index):
        return self.stacked.widget(index)

    def count(self):
        """Return the number of editors opened."""
        return self.stacked.count()

    def _update_cursor_position(self, line=0, col=0, ignore_sender=False):
        obj = self.sender()
        editor = self.current_editor()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            line += 1
            self.bar.update_line_col(line, col)

    def _set_current_symbol(self, line, ignore_sender=False):
        obj = self.sender()
        editor = self.current_editor()
        # Check if it's current to avoid signals from other splits.
        if ignore_sender or editor == obj:
            index = bisect.bisect(self._symbols_index, line)
            if (index >= len(self._symbols_index) or
                    self._symbols_index[index] > (line + 1)):
                index -= 1
            self.bar.set_current_symbol(index)

    def _editor_modified(self, value, sender=None):
        if sender is None:
            sender = self.sender()
        neditable = sender.neditable
        if value:
            text = "\u2022 %s" % neditable.display_name
            self.bar.update_item_text(neditable, text)
        else:
            self.bar.update_item_text(neditable, neditable.display_name)

    def _go_to_symbol(self, index):
        line = self._symbols_index[index]
        editor = self.current_editor()
        editor.go_to_line(line, center=True)
        editor.setFocus()

    def _update_symbols(self, neditable):
        editor = self.current_editor()
        # Check if it's current to avoid signals from other splits.
        if editor.neditable == neditable:
            self._load_symbols(neditable)

    def _update_combo_info(self, neditable):
        self.bar.update_item_text(neditable, neditable.display_name)
        self._main_container.current_editor_changed(neditable.file_path)

    def _load_symbols(self, neditable):
        # Get symbols handler by language
        symbols_handler = handlers.get_symbols_handler(neditable.language())
        if symbols_handler is None:
            return
        source = neditable.editor.text
        source = source.encode(neditable.editor.encoding)
        symbols, symbols_simplified = symbols_handler.obtain_symbols(
            source, simple=True)
        self._symbols_index = sorted(symbols_simplified.keys())
        symbols_simplified = sorted(
            list(symbols_simplified.items()), key=lambda x: x[0])
        self.bar.add_symbols(symbols_simplified)
        line, _ = neditable.editor.cursor_position
        self._set_current_symbol(line, True)
        tree_symbols = IDE.get_service('symbols_explorer')
        if tree_symbols is not None:
            tree_symbols.update_symbols_tree(symbols, neditable.file_path)

    def _show_notification_icon(self, neditable):
        checkers = neditable.sorted_checkers
        icon = QIcon()
        for items in checkers:
            checker, color, _ = items
            if checker.checks:
                if isinstance(checker.checker_icon, int):
                    icon = self.style().standardIcon(checker.checker_icon)
                elif isinstance(checker.checker_icon, str):
                    icon = QIcon(checker.checker_icon)
                # FIXME: sucks
                else:
                    icon = QIcon(checker.checker_icon)
                break
        self.bar.update_item_icon(neditable, icon)

    def show_menu_navigation(self):
        self.bar.code_navigator.show_menu_navigation()

    def closeEvent(self, event):
        self.about_to_close_combo_editor.emit()
        # self.emit(SIGNAL("aboutToCloseComboEditor()"))
        super(ComboEditor, self).closeEvent(event)

    def reject(self):
        if not self.__original:
            super(ComboEditor, self).reject()
Esempio n. 41
0
class Preferences(QDialog):
#
    configuration = {}
    weight = 0
#
    savePreferences = pyqtSignal()
#
    def __init__(self, parent=None):
        super(Preferences, self).__init__(parent, Qt.Dialog)
        self.setWindowTitle(translations.TR_PREFERENCES_TITLE)
        self.setMinimumSize(QSize(900, 600))
        vbox = QVBoxLayout(self)
        hbox = QHBoxLayout()
        vbox.setContentsMargins(0, 0, 5, 5)
        hbox.setContentsMargins(0, 0, 0, 0)
#
        self.tree = QTreeWidget()
        self.tree.header().setHidden(True)
        self.tree.setSelectionMode(QTreeWidget.SingleSelection)
        self.tree.setAnimated(True)
        self.tree.header().setHorizontalScrollMode(
            QAbstractItemView.ScrollPerPixel)
        self.tree.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)
        self.tree.header().setStretchLastSection(False)
        self.tree.setFixedWidth(200)
        self.stacked = QStackedLayout()
        hbox.addWidget(self.tree)
        hbox.addLayout(self.stacked)
        vbox.addLayout(hbox)
#
        hbox_footer = QHBoxLayout()
        self._btnSave = QPushButton(translations.TR_SAVE)
        self._btnCancel = QPushButton(translations.TR_CANCEL)
        hbox_footer.addSpacerItem(QSpacerItem(1, 0, QSizePolicy.Expanding))
        hbox_footer.addWidget(self._btnCancel)
        hbox_footer.addWidget(self._btnSave)
        vbox.addLayout(hbox_footer)
#
        self.tree.itemSelectionChanged.connect(self._change_current)
        self._btnCancel.clicked['bool'].connect(self.close)
        self._btnSave.clicked['bool'].connect(self._save_preferences)
#
        self.load_ui()
        self.tree.setCurrentItem(self.tree.topLevelItem(0))
#
    def _save_preferences(self):
        self.savePreferences.emit()
        self.close()
#
    def load_ui(self):
        sections = sorted(
            list(Preferences.configuration.keys()),
            key=lambda item: Preferences.configuration[item]['weight'])
        for section in sections:
            text = Preferences.configuration[section]['text']
            Widget = Preferences.configuration[section]['widget']
            widget = Widget(self)
            area = QScrollArea()
            area.setWidgetResizable(True)
            area.setWidget(widget)
            self.stacked.addWidget(area)
            index = self.stacked.indexOf(area)
            item = QTreeWidgetItem([text])
            item.setData(0, Qt.UserRole, index)
            self.tree.addTopLevelItem(item)
#
            #Sort Item Children
            subcontent = Preferences.configuration[section].get(
                'subsections', {})
            subsections = sorted(list(subcontent.keys()),
                                 key=lambda item: subcontent[item]['weight'])
            for sub in subsections:
                text = subcontent[sub]['text']
                Widget = subcontent[sub]['widget']
                widget = Widget(self)
                area = QScrollArea()
                area.setWidgetResizable(True)
                area.setWidget(widget)
                self.stacked.addWidget(area)
                index = self.stacked.indexOf(area)
                subitem = QTreeWidgetItem([text])
                subitem.setData(0, Qt.UserRole, index)
                item.addChild(subitem)
#
        self.tree.expandAll()
#
    def _change_current(self):
        item = self.tree.currentItem()
        index = item.data(0, Qt.UserRole)
        self.stacked.setCurrentIndex(index)
#
    @classmethod
    def register_configuration(cls, section, widget, text, weight=None,
                               subsection=None):
        if weight is None:
            Preferences.weight += 1
            weight = Preferences.weight
        if not subsection:
            Preferences.configuration[section] = {'widget': widget,
                                                  'weight': weight,
                                                  'text': text}
        else:
            config = Preferences.configuration.get(section, {})
            if not config:
                config[section] = {'widget': None, 'weight': 100}
            subconfig = config.get('subsections', {})
            subconfig[subsection] = {'widget': widget, 'weight': weight,
                                     'text': text}
            config['subsections'] = subconfig
            Preferences.configuration[section] = config
Esempio n. 42
0
class OptionsWidget(QWidget):

    """
    Widget holds menu with all options.
    """

    def __init__(self, main_window, items):

        super().__init__()

        self.items = items

        self.layout = QStackedLayout()
        self._switch_user = self._get_switch_user_func(main_window)
        self.setLayout(self.layout)
        self._hide_action_button = lambda: main_window.communication.action_button_toggle.emit(False, '', None)

        self._create_layout(main_window)

    def set_current_index(self, index):
        self.layout.setCurrentIndex(index)
        if not index:
            self._hide_action_button()

    def showEvent(self, event):
        if not self.layout.currentIndex():
            self._hide_action_button()

    def _get_switch_user_func(self, main_window):
        def _switch_user():
            main_window.menu_btn_clicked(main_window.user_frame_index)
            self.layout.setCurrentIndex(0)
        return _switch_user

    def _get_template_import_func(self, main_window):
        func = self._wrap_template_func(template.Template.import_, main_window)

        def _alert_callback(path, value):
            if value:
                func(path[0])

        def _f():
            path = QFileDialog.getOpenFileName(main_window, 'Выберите файл', options.DATABASE_DIR)
            if path:
                main_window.create_alert('Шаблоны с одинаковыми именами будут перезаписаны.'
                                         '\nПродолжить?', functools.partial(_alert_callback, path))
        return _f

    def _get_template_export_func(self, main_window):
        func = self._wrap_template_func(template.Template.export, main_window)

        def _f():
            path = QFileDialog.getExistingDirectory(main_window, 'Выберите путь', options.DATABASE_DIR)
            if path:
                return func(path)
        return _f

    @staticmethod
    def _wrap_template_func(func, main_window):
        def _f(path):
            ok, result = func(path)
            if ok:
                main_window.show_message('Готово')
            else:
                main_window.create_alert('Произошла ошибка\n{}'.format(result.get('error')))
            return ok, result
        return _f

    def _create_layout(self, main_window):

        wrapper = QHBoxLayout()
        self.layout.addWidget(utils.get_scrollable(wrapper))
        rows = 8
        cols = 3
        vboxes = [QVBoxLayout() for _ in range(cols)]

        widgets = ((TemplateWidgetInOptions(main_window, self.items, self), 'Шаблоны'),
                   (UsersAndGroupsWidget(main_window, self), 'Пользователи и группы'),
                   (self._switch_user, 'Сменить пользователя'),
                   (self._get_template_export_func(main_window), 'Экспортировать шаблоны'),
                   (self._get_template_import_func(main_window), 'Импортировать шаблоны'))

        for i, widget in enumerate(widgets):
            b = QPushButton(widget[1])

            if callable(widget[0]):
                b.clicked.connect(widget[0])
            else:
                b.clicked.connect(functools.partial(self.layout.setCurrentIndex, i + 1))
                self.layout.addWidget(widget[0])

            b.setGraphicsEffect(utils.get_shadow())
            vboxes[(i // rows) % cols].addWidget(b)

        for each in vboxes:
            each.addStretch()
            wrapper.addLayout(each, stretch=int(100 / cols))
# Make widgets

window = QWidget()

btn1 = QPushButton("One")
btn2 = QPushButton("Two")
btn3 = QPushButton("Three")

# Set the layout

stack = QStackedLayout()

stack.addWidget(btn1)
stack.addWidget(btn2)
stack.addWidget(btn3)

stack.setCurrentIndex(1)

window.setLayout(stack)

# Show
window.show()

# The mainloop of the application. The event handling starts from this point.
# The exec_() method has an underscore. It is because the exec is a Python keyword. And thus, exec_() was used instead.
exit_code = app.exec_()

# The sys.exit() method ensures a clean exit.
# The environment will be informed, how the application ended.
sys.exit(exit_code)
Esempio n. 44
0
class AppWindow(QMainWindow):
    "The application's main window"

    def __init__(self):
        super().__init__()
        self.initUI()
        self.start_up()
        QTimer.singleShot(3000, self._check_update)

    def init_watchers(self):
        def remove_gallery(g):
            index = self.manga_list_view.find_index(g.id)
            if index:
                self.manga_list_view.remove_gallery([index])

        def create_gallery(path):
            g_dia = gallerydialog.GalleryDialog(self, path)
            g_dia.SERIES.connect(self.manga_list_view.gallery_model.addRows)
            g_dia.show()

        def update_gallery(g):
            index = self.manga_list_view.find_index(g.id)
            if index:
                self.manga_list_view.replace_edit_gallery([g], index.row())
            else:
                log_e("Could not find gallery to update from Watcher")

        def created(path):
            c_popup = file_misc.CreatedPopup(path, self)
            c_popup.ADD_SIGNAL.connect(create_gallery)

        def modified(path, gallery):
            mod_popup = file_misc.ModifiedPopup(path, gallery, self)

        def deleted(path, gallery):
            d_popup = file_misc.DeletedPopup(path, gallery, self)
            d_popup.UPDATE_SIGNAL.connect(update_gallery)
            d_popup.REMOVE_SIGNAL.connect(remove_gallery)

        def moved(new_path, gallery):
            mov_popup = file_misc.MovedPopup(new_path, gallery, self)
            mov_popup.UPDATE_SIGNAL.connect(update_gallery)

        self.watchers = file_misc.Watchers()
        self.watchers.gallery_handler.CREATE_SIGNAL.connect(created)
        self.watchers.gallery_handler.MODIFIED_SIGNAL.connect(modified)
        self.watchers.gallery_handler.MOVED_SIGNAL.connect(moved)
        self.watchers.gallery_handler.DELETED_SIGNAL.connect(deleted)

        if gui_constants.LOOK_NEW_GALLERY_STARTUP:
            self.notification_bar.add_text("Looking for new galleries...")
            try:

                class ScanDir(QObject):
                    final_paths_and_galleries = pyqtSignal(list, list)

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

                    def scan_dirs(self):
                        db_data = self.model_data
                        paths = []
                        for g in range(len(db_data)):
                            paths.append(os.path.normcase(db_data[g].path))

                        contents = []
                        case_path = []  # needed for tile and artist parsing... e.g to avoid lowercase
                        for m_path in gui_constants.MONITOR_PATHS:
                            for p in os.listdir(m_path):
                                abs_p = os.path.join(m_path, p)
                                if os.path.isdir(abs_p) or p.endswith(utils.ARCHIVE_FILES):
                                    case_path.append(abs_p)
                                    contents.append(os.path.normcase(abs_p))

                        paths = sorted(paths)
                        new_galleries = []
                        for c, x in enumerate(contents):
                            y = utils.b_search(paths, x)
                            if not y:
                                # (path, number for case_path)
                                new_galleries.append((x, c))

                        galleries = []
                        final_paths = []
                        if new_galleries:
                            for g in new_galleries:
                                gallery = gallerydb.Gallery()
                                try:
                                    gallery.profile = utils.get_gallery_img(g[0])
                                except:
                                    gallery.profile = gui_constants.NO_IMAGE_PATH
                                parser_dict = utils.title_parser(os.path.split(case_path[g[1]])[1])
                                gallery.title = parser_dict["title"]
                                gallery.artist = parser_dict["artist"]
                                galleries.append(gallery)
                                final_paths.append(case_path[g[1]])
                        self.final_paths_and_galleries.emit(final_paths, galleries)
                        # if gui_constants.LOOK_NEW_GALLERY_AUTOADD:
                        # 	QTimer.singleShot(10000, self.gallery_populate(final_paths))
                        # 	return

                def show_new_galleries(final_paths, galleries):
                    if final_paths and galleries:
                        if gui_constants.LOOK_NEW_GALLERY_AUTOADD:
                            self.gallery_populate(final_paths)
                        else:
                            if len(galleries) == 1:
                                self.notification_bar.add_text(
                                    "{} new gallery was discovered in one of your monitored directories".format(
                                        len(galleries)
                                    )
                                )
                            else:
                                self.notification_bar.add_text(
                                    "{} new galleries were discovered in one of your monitored directories".format(
                                        len(galleries)
                                    )
                                )
                            text = (
                                "These new galleries were discovered! Do you want to add them?"
                                if len(galleries) > 1
                                else "This new gallery was discovered! Do you want to add it?"
                            )
                            g_popup = file_misc.GalleryPopup((text, galleries), self)
                            buttons = g_popup.add_buttons("Add", "Close")

                            def populate_n_close():
                                self.gallery_populate(final_paths)
                                g_popup.close()

                            buttons[0].clicked.connect(populate_n_close)
                            buttons[1].clicked.connect(g_popup.close)

                thread = QThread(self)
                self.scan_inst = ScanDir(self.manga_list_view.gallery_model._data)
                self.scan_inst.moveToThread(thread)
                self.scan_inst.final_paths_and_galleries.connect(show_new_galleries)
                self.scan_inst.final_paths_and_galleries.connect(lambda a: self.scan_inst.deleteLater())
                thread.started.connect(self.scan_inst.scan_dirs)
                thread.finished.connect(thread.deleteLater)
                thread.start()
            except:
                self.notification_bar.add_text(
                    "An error occured while attempting to scan for new galleries. Check happypanda.log."
                )
                log.exception("An error occured while attempting to scan for new galleries.")

    def start_up(self):
        def normalize_first_time():
            settings.set(2, "Application", "first time level")

        def done():
            self.manga_list_view.gallery_model.init_data()
            if gui_constants.ENABLE_MONITOR and gui_constants.MONITOR_PATHS and all(gui_constants.MONITOR_PATHS):
                self.init_watchers()
            if gui_constants.FIRST_TIME_LEVEL != 2:
                normalize_first_time()

        if gui_constants.FIRST_TIME_LEVEL < 2:

            class FirstTime(file_misc.BasePopup):
                def __init__(self, parent=None):
                    super().__init__(parent)
                    main_layout = QVBoxLayout()
                    info_lbl = QLabel(
                        "Hi there! Some big changes are about to occur!\n"
                        + "Please wait.. This will take at most a few minutes.\n"
                        + "If not then try restarting the application."
                    )
                    info_lbl.setAlignment(Qt.AlignCenter)
                    main_layout.addWidget(info_lbl)
                    prog = QProgressBar(self)
                    prog.setMinimum(0)
                    prog.setMaximum(0)
                    prog.setTextVisible(False)
                    main_layout.addWidget(prog)
                    main_layout.addWidget(QLabel("Note: This popup will close itself when everything is ready"))
                    self.main_widget.setLayout(main_layout)

            ft_widget = FirstTime(self)
            log_i("Invoking first time level 2")
            bridge = gallerydb.Bridge()
            thread = QThread(self)
            thread.setObjectName("Startup")
            bridge.moveToThread(thread)
            thread.started.connect(bridge.rebuild_galleries)
            bridge.DONE.connect(ft_widget.close)
            bridge.DONE.connect(self.setEnabled)
            bridge.DONE.connect(done)
            bridge.DONE.connect(bridge.deleteLater)
            thread.finished.connect(thread.deleteLater)
            thread.start()
            ft_widget.adjustSize()
            ft_widget.show()
            self.setEnabled(False)
        else:
            done()

    def initUI(self):
        self.center = QWidget()
        self.display = QStackedLayout()
        self.center.setLayout(self.display)
        # init the manga view variables
        self.manga_display()
        log_d("Create manga display: OK")
        # init the chapter view variables
        # self.chapter_display()
        self.m_l_view_index = self.display.addWidget(self.manga_list_main)
        self.m_t_view_index = self.display.addWidget(self.manga_table_view)
        # init toolbar
        self.init_toolbar()
        log_d("Create toolbar: OK")
        # init status bar
        self.init_stat_bar()
        log_d("Create statusbar: OK")

        self.system_tray = misc.SystemTray(QIcon(gui_constants.APP_ICO_PATH), self)
        gui_constants.SYSTEM_TRAY = self.system_tray
        tray_menu = QMenu(self)
        self.system_tray.setContextMenu(tray_menu)
        self.system_tray.setToolTip("Happypanda {}".format(gui_constants.vs))
        tray_quit = QAction("Quit", tray_menu)
        tray_menu.addAction(tray_quit)
        tray_quit.triggered.connect(self.close)
        self.system_tray.show()
        log_d("Create system tray: OK")

        # self.display.addWidget(self.chapter_main)

        self.setCentralWidget(self.center)
        self.setWindowTitle("Happypanda")
        self.setWindowIcon(QIcon(gui_constants.APP_ICO_PATH))

        props = settings.win_read(self, "AppWindow")
        if props.resize:
            x, y = props.resize
            self.resize(x, y)
        else:
            self.resize(gui_constants.MAIN_W, gui_constants.MAIN_H)
        posx, posy = props.pos
        self.move(posx, posy)
        self.show()
        log_d("Show window: OK")

        self.notification_bar = misc.NotificationOverlay(self)
        p = self.toolbar.pos()
        self.notification_bar.move(p.x(), p.y() + self.toolbar.height())
        self.notification_bar.resize(self.width())
        gui_constants.NOTIF_BAR = self.notification_bar
        log_d("Create notificationbar: OK")

        log_d("Window Create: OK")

    def _check_update(self):
        class upd_chk(QObject):
            UPDATE_CHECK = pyqtSignal(str)

            def __init__(self, **kwargs):
                super().__init__(**kwargs)

            def fetch_vs(self):
                import requests
                import time

                try:
                    log_d("Checking Update")
                    time.sleep(1.5)
                    r = requests.get(
                        "https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt", verify="cacert.pem"
                    )
                    a = r.text
                    vs = a.strip()
                    self.UPDATE_CHECK.emit(vs)
                except:
                    log.exception("Checking Update: FAIL")
                    self.UPDATE_CHECK.emit("this is a very long text which is is sure to be over limit")

        def check_update(vs):
            log_i("Received version: {}\nCurrent version: {}".format(vs, gui_constants.vs))
            if vs != gui_constants.vs:
                if len(vs) < 10:
                    self.notification_bar.add_text(
                        "Version {} of Happypanda is".format(vs) + " available. Click here to update!", False
                    )
                    self.notification_bar.clicked.connect(
                        lambda: utils.open_web_link("https://github.com/Pewpews/happypanda/releases")
                    )
                    self.notification_bar.set_clickable(True)
                else:
                    self.notification_bar.add_text("An error occurred while checking for new version")

        self.update_instance = upd_chk()
        thread = QThread(self)
        self.update_instance.moveToThread(thread)
        thread.started.connect(self.update_instance.fetch_vs)
        self.update_instance.UPDATE_CHECK.connect(check_update)
        self.update_instance.UPDATE_CHECK.connect(self.update_instance.deleteLater)
        thread.finished.connect(thread.deleteLater)
        thread.start()

    def _web_metadata_picker(self, gallery, title_url_list, queue, parent=None):
        if not parent:
            parent = self
        text = "Which gallery do you want to extract metadata from?"
        s_gallery_popup = misc.SingleGalleryChoices(gallery, title_url_list, text, parent)
        s_gallery_popup.USER_CHOICE.connect(queue.put)

    def get_metadata(self, gal=None):
        thread = QThread(self)
        thread.setObjectName("App.get_metadata")
        fetch_instance = fetch.Fetch()
        if gal:
            galleries = [gal]
        else:
            if gui_constants.CONTINUE_AUTO_METADATA_FETCHER:
                galleries = [g for g in self.manga_list_view.gallery_model._data if not g.exed]
            else:
                galleries = self.manga_list_view.gallery_model._data
            if not galleries:
                self.notification_bar.add_text("Looks like we've already gone through all galleries!")
                return None
        fetch_instance.galleries = galleries

        self.notification_bar.begin_show()
        fetch_instance.moveToThread(thread)

        def done(status):
            self.notification_bar.end_show()
            fetch_instance.deleteLater()

        fetch_instance.GALLERY_PICKER.connect(self._web_metadata_picker)
        fetch_instance.GALLERY_EMITTER.connect(self.manga_list_view.replace_edit_gallery)
        fetch_instance.AUTO_METADATA_PROGRESS.connect(self.notification_bar.add_text)
        thread.started.connect(fetch_instance.auto_web_metadata)
        fetch_instance.FINISHED.connect(done)
        thread.finished.connect(thread.deleteLater)
        thread.start()

        # def style_tooltip(self):
        # 	palette = QToolTip.palette()
        # 	palette.setColor()

    def init_stat_bar(self):
        self.status_bar = self.statusBar()
        self.status_bar.setMaximumHeight(20)
        self.status_bar.setSizeGripEnabled(False)
        self.stat_info = QLabel()
        self.stat_info.setIndent(5)
        self.sort_main = QAction("Asc", self)
        sort_menu = QMenu()
        self.sort_main.setMenu(sort_menu)
        s_by_title = QAction("Title", sort_menu)
        s_by_artist = QAction("Artist", sort_menu)
        sort_menu.addAction(s_by_title)
        sort_menu.addAction(s_by_artist)
        self.status_bar.addPermanentWidget(self.stat_info)
        # self.status_bar.addAction(self.sort_main)
        self.temp_msg = QLabel()
        self.temp_timer = QTimer()

        self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.connect(self.stat_row_info)
        self.manga_list_view.gallery_model.STATUSBAR_MSG.connect(self.stat_temp_msg)
        self.manga_list_view.STATUS_BAR_MSG.connect(self.stat_temp_msg)
        self.manga_table_view.STATUS_BAR_MSG.connect(self.stat_temp_msg)
        self.stat_row_info()

    def stat_temp_msg(self, msg):
        self.temp_timer.stop()
        self.temp_msg.setText(msg)
        self.status_bar.addWidget(self.temp_msg)
        self.temp_timer.timeout.connect(self.temp_msg.clear)
        self.temp_timer.setSingleShot(True)
        self.temp_timer.start(5000)

    def stat_row_info(self):
        r = self.manga_list_view.model().rowCount()
        t = self.manga_list_view.gallery_model._data_count
        self.stat_info.setText("Loaded {} of {} ".format(r, t))

    def manga_display(self):
        "initiates the manga view"
        # list view
        self.manga_list_main = QWidget()
        # self.manga_list_main.setContentsMargins(-10, -12, -10, -10)
        self.manga_list_main.setContentsMargins(10, -9, -10, -10)  # x, y, inverted_width, inverted_height
        self.manga_list_layout = QHBoxLayout()
        self.manga_list_main.setLayout(self.manga_list_layout)

        self.manga_list_view = gallery.MangaView(self)
        self.manga_list_view.clicked.connect(self.popup)
        self.manga_list_view.manga_delegate.POPUP.connect(self.popup)
        self.popup_window = self.manga_list_view.manga_delegate.popup_window
        self.manga_list_layout.addWidget(self.manga_list_view)

        # table view
        self.manga_table_main = QWidget()
        self.manga_table_layout = QVBoxLayout()
        self.manga_table_main.setLayout(self.manga_table_layout)

        self.manga_table_view = gallery.MangaTableView(self)
        self.manga_table_view.gallery_model = self.manga_list_view.gallery_model
        self.manga_table_view.sort_model = self.manga_list_view.sort_model
        self.manga_table_view.setModel(self.manga_table_view.sort_model)
        self.manga_table_view.sort_model.change_model(self.manga_table_view.gallery_model)
        self.manga_table_view.setColumnWidth(gui_constants.FAV, 20)
        self.manga_table_view.setColumnWidth(gui_constants.ARTIST, 200)
        self.manga_table_view.setColumnWidth(gui_constants.TITLE, 400)
        self.manga_table_view.setColumnWidth(gui_constants.TAGS, 300)
        self.manga_table_view.setColumnWidth(gui_constants.TYPE, 60)
        self.manga_table_view.setColumnWidth(gui_constants.CHAPTERS, 60)
        self.manga_table_view.setColumnWidth(gui_constants.LANGUAGE, 100)
        self.manga_table_view.setColumnWidth(gui_constants.LINK, 400)
        self.manga_table_layout.addWidget(self.manga_table_view)

    def search(self, srch_string):
        case_ins = srch_string.lower()
        if not gui_constants.ALLOW_SEARCH_REGEX:
            remove = "^$*+?{}\\|()[]"
            for x in remove:
                if x == "[" or x == "]":
                    continue
                else:
                    case_ins = case_ins.replace(x, ".")
        else:
            try:
                re.compile(case_ins)
            except re.error:
                return
        self.manga_list_view.sort_model.search(case_ins)

    def popup(self, index):
        if not self.popup_window.isVisible():
            m_x = QCursor.pos().x()
            m_y = QCursor.pos().y()
            d_w = QDesktopWidget().width()
            d_h = QDesktopWidget().height()
            p_w = gui_constants.POPUP_WIDTH
            p_h = gui_constants.POPUP_HEIGHT

            index_rect = self.manga_list_view.visualRect(index)
            index_point = self.manga_list_view.mapToGlobal(index_rect.topRight())
            # adjust so it doesn't go offscreen
            if d_w - m_x < p_w and d_h - m_y < p_h:  # bottom
                self.popup_window.move(m_x - p_w + 5, m_y - p_h)
            elif d_w - m_x > p_w and d_h - m_y < p_h:
                self.popup_window.move(m_x + 5, m_y - p_h)
            elif d_w - m_x < p_w:
                self.popup_window.move(m_x - p_w + 5, m_y + 5)
            else:
                self.popup_window.move(index_point)

            self.popup_window.set_gallery(index.data(Qt.UserRole + 1))
            self.popup_window.show()

    def favourite_display(self):
        "Switches to favourite display"
        if self.display.currentIndex() == self.m_l_view_index:
            self.manga_list_view.sort_model.fav_view()
        else:
            self.manga_table_view.sort_model.fav_view()

    def catalog_display(self):
        "Switches to catalog display"
        if self.display.currentIndex() == self.m_l_view_index:
            self.manga_list_view.sort_model.catalog_view()
        else:
            self.manga_table_view.sort_model.catalog_view()

    def settings(self):
        sett = settingsdialog.SettingsDialog(self)
        sett.scroll_speed_changed.connect(self.manga_list_view.updateGeometries)
        # sett.show()

    def init_toolbar(self):
        self.toolbar = QToolBar()
        self.toolbar.setFixedHeight(25)
        self.toolbar.setWindowTitle("Show")  # text for the contextmenu
        # self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined?
        self.toolbar.setMovable(False)
        self.toolbar.setFloatable(False)
        # self.toolbar.setIconSize(QSize(20,20))
        self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        spacer_start = QWidget()  # aligns the first actions properly
        spacer_start.setFixedSize(QSize(10, 1))
        self.toolbar.addWidget(spacer_start)

        favourite_view_icon = QIcon(gui_constants.STAR_BTN_PATH)
        favourite_view_action = QAction(favourite_view_icon, "Favorites", self)
        favourite_view_action.setToolTip("Show only favourite galleries")
        favourite_view_action.triggered.connect(self.favourite_display)  # need lambda to pass extra args
        self.toolbar.addAction(favourite_view_action)

        catalog_view_icon = QIcon(gui_constants.HOME_BTN_PATH)
        catalog_view_action = QAction(catalog_view_icon, "Library", self)
        catalog_view_action.setToolTip("Show all your galleries")
        # catalog_view_action.setText("Catalog")
        catalog_view_action.triggered.connect(self.catalog_display)  # need lambda to pass extra args
        self.toolbar.addAction(catalog_view_action)
        self.toolbar.addSeparator()

        gallery_menu = QMenu()
        gallery_action = QToolButton()
        gallery_action.setText("Gallery ")
        gallery_action.setPopupMode(QToolButton.InstantPopup)
        gallery_action.setToolTip("Contains various gallery related features")
        gallery_action.setMenu(gallery_menu)
        add_gallery_icon = QIcon(gui_constants.PLUS_PATH)
        gallery_action_add = QAction(add_gallery_icon, "Add gallery", self)
        gallery_action_add.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit)
        gallery_action_add.setToolTip("Add a single gallery thoroughly")
        gallery_menu.addAction(gallery_action_add)
        add_more_action = QAction(add_gallery_icon, "Add galleries...", self)
        add_more_action.setStatusTip("Add galleries from different folders")
        add_more_action.triggered.connect(lambda: self.populate(True))
        gallery_menu.addAction(add_more_action)
        populate_action = QAction(add_gallery_icon, "Populate from folder...", self)
        populate_action.setStatusTip("Populates the DB with galleries from a single folder")
        populate_action.triggered.connect(self.populate)
        gallery_menu.addAction(populate_action)
        gallery_menu.addSeparator()
        metadata_action = QAction("Get metadata for all galleries", self)
        metadata_action.triggered.connect(self.get_metadata)
        gallery_menu.addAction(metadata_action)
        self.toolbar.addWidget(gallery_action)
        self.toolbar.addSeparator()

        misc_action = QToolButton()
        misc_action.setText("Misc ")
        misc_action_menu = QMenu()
        misc_action.setMenu(misc_action_menu)
        misc_action.setPopupMode(QToolButton.InstantPopup)
        misc_action.setToolTip("Contains misc. features")
        misc_action_random = QAction("Open random gallery", misc_action_menu)
        misc_action_random.triggered.connect(self.manga_list_view.open_random_gallery)
        misc_action_menu.addAction(misc_action_random)
        duplicate_check_simple = QAction("Simple duplicate finder", misc_action_menu)
        duplicate_check_simple.triggered.connect(lambda: self.manga_list_view.duplicate_check())
        misc_action_menu.addAction(duplicate_check_simple)
        self.toolbar.addWidget(misc_action)

        spacer_middle = QWidget()  # aligns buttons to the right
        spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.toolbar.addWidget(spacer_middle)

        self.grid_toggle_g_icon = QIcon(gui_constants.GRID_PATH)
        self.grid_toggle_l_icon = QIcon(gui_constants.LIST_PATH)
        self.grid_toggle = QToolButton()
        if self.display.currentIndex() == self.m_l_view_index:
            self.grid_toggle.setIcon(self.grid_toggle_l_icon)
        else:
            self.grid_toggle.setIcon(self.grid_toggle_g_icon)
        self.grid_toggle.setObjectName("gridtoggle")
        self.grid_toggle.clicked.connect(self.toggle_view)
        self.toolbar.addWidget(self.grid_toggle)

        self.search_bar = misc.LineEdit()
        if gui_constants.SEARCH_AUTOCOMPLETE:
            completer = QCompleter(self)
            completer.setModel(self.manga_list_view.gallery_model)
            completer.setCaseSensitivity(Qt.CaseInsensitive)
            completer.setCompletionMode(QCompleter.PopupCompletion)
            completer.setCompletionRole(Qt.DisplayRole)
            completer.setCompletionColumn(gui_constants.TITLE)
            completer.setFilterMode(Qt.MatchContains)
            self.search_bar.setCompleter(completer)
        if gui_constants.SEARCH_ON_ENTER:
            self.search_bar.returnPressed.connect(lambda: self.search(self.search_bar.text()))
        else:
            self.search_bar.textChanged[str].connect(self.search)
        self.search_bar.setPlaceholderText("Search title, artist, namespace & tags")
        self.search_bar.setMinimumWidth(150)
        self.search_bar.setMaximumWidth(500)
        self.toolbar.addWidget(self.search_bar)
        self.toolbar.addSeparator()
        settings_icon = QIcon(gui_constants.SETTINGS_PATH)
        settings_action = QAction("Set&tings", self)
        settings_action.triggered.connect(self.settings)
        self.toolbar.addAction(settings_action)

        spacer_end = QWidget()  # aligns About action properly
        spacer_end.setFixedSize(QSize(10, 1))
        self.toolbar.addWidget(spacer_end)
        self.addToolBar(self.toolbar)

    def toggle_view(self):
        """
		Toggles the current display view
		"""
        if self.display.currentIndex() == self.m_l_view_index:
            self.display.setCurrentIndex(self.m_t_view_index)
            self.grid_toggle.setIcon(self.grid_toggle_g_icon)
        else:
            self.display.setCurrentIndex(self.m_l_view_index)
            self.grid_toggle.setIcon(self.grid_toggle_l_icon)

            # TODO: Improve this so that it adds to the gallery dialog,
            # so user can edit data before inserting (make it a choice)

    def populate(self, mixed=None):
        "Populates the database with gallery from local drive'"
        if mixed:
            gallery_view = misc.GalleryListView(self, True)
            gallery_view.SERIES.connect(self.gallery_populate)
            gallery_view.show()
        else:
            path = QFileDialog.getExistingDirectory(None, "Choose a folder containing your galleries")
            self.gallery_populate(path, True)

    def gallery_populate(self, path, validate=False):
        "Scans the given path for gallery to add into the DB"
        if len(path) is not 0:
            data_thread = QThread(self)
            data_thread.setObjectName("General gallery populate")
            loading = misc.Loading(self)
            if not loading.ON:
                misc.Loading.ON = True
                fetch_instance = fetch.Fetch()
                fetch_instance.series_path = path
                loading.show()

                def finished(status):
                    def hide_loading():
                        loading.hide()

                    if status:
                        if len(status) != 0:

                            def add_gallery(gallery_list):
                                def append_to_model(x):
                                    self.manga_list_view.gallery_model.insertRows(x, None, len(x))

                                class A(QObject):
                                    done = pyqtSignal()
                                    prog = pyqtSignal(int)

                                    def __init__(self, obj, parent=None):
                                        super().__init__(parent)
                                        self.obj = obj
                                        self.galleries = []

                                    def add_to_db(self):
                                        gui_constants.NOTIF_BAR.begin_show()
                                        gui_constants.NOTIF_BAR.add_text("Populating database...")
                                        for y, x in enumerate(self.obj):
                                            gui_constants.NOTIF_BAR.add_text(
                                                "Populating database {}/{}".format(y + 1, len(self.obj))
                                            )
                                            gallerydb.add_method_queue(gallerydb.GalleryDB.add_gallery_return, False, x)
                                            self.galleries.append(x)
                                            y += 1
                                            self.prog.emit(y)
                                        append_to_model(self.galleries)
                                        gui_constants.NOTIF_BAR.end_show()
                                        self.done.emit()

                                loading.progress.setMaximum(len(gallery_list))
                                a_instance = A(gallery_list)
                                thread = QThread(self)
                                thread.setObjectName("Database populate")

                                def loading_show():
                                    loading.setText("Populating database.\nPlease wait...")
                                    loading.show()

                                def loading_hide():
                                    loading.close()
                                    self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.emit()

                                def del_later():
                                    try:
                                        a_instance.deleteLater()
                                    except NameError:
                                        pass

                                a_instance.moveToThread(thread)
                                a_instance.prog.connect(loading.progress.setValue)
                                thread.started.connect(loading_show)
                                thread.started.connect(a_instance.add_to_db)
                                a_instance.done.connect(loading_hide)
                                a_instance.done.connect(del_later)
                                thread.finished.connect(thread.deleteLater)
                                thread.start()

                            data_thread.quit
                            hide_loading()
                            log_i("Populating DB from gallery folder: OK")
                            if validate:
                                gallery_list = misc.GalleryListView(self)
                                gallery_list.SERIES.connect(add_gallery)
                                for ser in status:
                                    gallery_list.add_gallery(ser, os.path.split(ser.path)[1])
                                    # self.manga_list_view.gallery_model.populate_data()
                                gallery_list.show()
                            else:
                                add_gallery(status)
                            misc.Loading.ON = False
                        else:
                            log_d("No new gallery was found")
                            loading.setText("No new gallery found")
                            data_thread.quit
                            misc.Loading.ON = False

                    else:
                        log_e("Populating DB from gallery folder: Nothing was added!")
                        loading.setText("<font color=red>Nothing was added. Check happypanda_log for details..</font>")
                        loading.progress.setStyleSheet("background-color:red;")
                        data_thread.quit
                        QTimer.singleShot(10000, loading.close)

                def fetch_deleteLater():
                    try:
                        fetch_instance.deleteLater
                    except NameError:
                        pass

                def a_progress(prog):
                    loading.progress.setValue(prog)
                    loading.setText("Searching for galleries...")

                fetch_instance.moveToThread(data_thread)
                fetch_instance.DATA_COUNT.connect(loading.progress.setMaximum)
                fetch_instance.PROGRESS.connect(a_progress)
                data_thread.started.connect(fetch_instance.local)
                fetch_instance.FINISHED.connect(finished)
                fetch_instance.FINISHED.connect(fetch_deleteLater)
                data_thread.finished.connect(data_thread.deleteLater)
                data_thread.start()
                log_i("Populating DB from gallery folder")

    def resizeEvent(self, event):
        try:
            self.notification_bar.resize(event.size().width())
        except AttributeError:
            pass
        return super().resizeEvent(event)

    def closeEvent(self, event):
        # watchers
        try:
            self.watchers.stop_all()
        except AttributeError:
            pass

            # settings
        settings.set(self.manga_list_view.current_sort, "General", "current sort")
        settings.win_save(self, "AppWindow")

        # temp dir
        try:
            for root, dirs, files in os.walk("temp", topdown=False):
                for name in files:
                    os.remove(os.path.join(root, name))
                for name in dirs:
                    os.rmdir(os.path.join(root, name))
            log_d("Empty temp on exit: OK")
        except:
            log_d("Empty temp on exit: FAIL")

            # error
        err = sys.exc_info()
        if all(err):
            log_c("Last error before exit:\n{}\n{}\n{}".format(err[0], err[1], err[2]))
        else:
            log_d("Normal Exit App: OK")
        super().closeEvent(event)
        app = QApplication.instance()
        app.quit()
        sys.exit()
class ProjectTreeColumn(QDialog):
    dockWidget = pyqtSignal("QObject*")
    undockWidget = pyqtSignal()
    changeTitle = pyqtSignal("QObject*", str)
    def __init__(self, parent=None):
        super(ProjectTreeColumn, self).__init__(parent,
                                                Qt.WindowStaysOnTopHint)
        vbox = QVBoxLayout(self)
        vbox.setSizeConstraint(QVBoxLayout.SetDefaultConstraint)
        vbox.setContentsMargins(0, 0, 0, 0)
        self._buttons = []

        self._combo_project = QComboBox()
        self._combo_project.setMinimumHeight(30)
        self._combo_project.setContextMenuPolicy(Qt.CustomContextMenu)
        vbox.addWidget(self._combo_project)

        self._projects_area = QStackedLayout()
        logger.debug("This is the projects area")
        logger.debug(self._projects_area)
        vbox.addLayout(self._projects_area)

        self.projects = []

        self._combo_project.currentIndexChanged[int].connect(self._change_current_project)
        self._combo_project.customContextMenuRequested['const QPoint &'].connect(self.context_menu_for_root)
        connections = (
            {'target': 'main_container',
             'signal_name': 'addToProject',#(QString)
             'slot': self._add_file_to_project},
            {'target': 'main_container',
             'signal_name': 'showFileInExplorer',#(QString)
             'slot': self._show_file_in_explorer},
        )
        IDE.register_service('projects_explorer', self)
        IDE.register_signals('projects_explorer', connections)
        ExplorerContainer.register_tab(translations.TR_TAB_PROJECTS, self)

        #FIXME: Should have a ninja settings object that stores tree state
        #FIXME: Or bettter, application data object
        #TODO: check this:
        #self.connect(ide, SIGNAL("goingDown()"),
            #self.tree_projects.shutdown)

        #def close_project_signal():
            #self.emit(SIGNAL("updateLocator()"))



    def install_tab(self):
        ide = IDE.getInstance()
        ui_tools.install_shortcuts(self, actions.PROJECTS_TREE_ACTIONS, ide)

        ide.goingDown.connect(self.close)

    def load_session_projects(self, projects):
        for project in projects:
            if os.path.exists(project):
                self._open_project_folder(project)

    def open_project_folder(self, folderName=None):
        print("open_project_folder", folderName)
        if settings.WORKSPACE:
            directory = settings.WORKSPACE
        else:
            directory = os.path.expanduser("~")

        if folderName is None:
            folderName = QFileDialog.getExistingDirectory(
                self, translations.TR_OPEN_PROJECT_DIRECTORY, directory)
            logger.debug("Choosing Foldername")
        if folderName:
            logger.debug("Opening %s" % folderName)
            self._open_project_folder(folderName)

    def _open_project_folder(self, folderName):
        ninjaide = IDE.get_service("ide")
        project = NProject(folderName)
        qfsm = ninjaide.filesystem.open_project(project)
        if qfsm:
            self.add_project(project)
            self.save_recent_projects(folderName)
            main_container = IDE.get_service('main_container')
            if main_container:
                main_container.show_editor_area()
            if len(self.projects) > 1:
                title = "%s (%s)" % (
                    translations.TR_TAB_PROJECTS, len(self.projects))
            else:
                title = translations.TR_TAB_PROJECTS
            self.changeTitle.emit(self, title)

    def _add_file_to_project(self, path):
        """Add the file for 'path' in the project the user choose here."""
        if self._projects_area.count() > 0:
            pathProject = [self.current_project]
            addToProject = add_to_project.AddToProject(pathProject, self)
            addToProject.exec_()
            if not addToProject.pathSelected:
                return
            main_container = IDE.get_service('main_container')
            if not main_container:
                return
            editorWidget = main_container.get_current_editor()
            if not editorWidget.file_path:
                name = QInputDialog.getText(None,
                                            translations.TR_ADD_FILE_TO_PROJECT,
                                            translations.TR_FILENAME + ": ")[0]
                if not name:
                    QMessageBox.information(
                        self,
                        translations.TR_INVALID_FILENAME,
                        translations.TR_INVALID_FILENAME_ENTER_A_FILENAME)
                    return
            else:
                name = file_manager.get_basename(editorWidget.file_path)
            new_path = file_manager.create_path(addToProject.pathSelected, name)
            ide_srv = IDE.get_service("ide")
            old_file = ide_srv.get_or_create_nfile(path)
            new_file = old_file.save(editorWidget.text(), new_path)
            #FIXME: Make this file replace the original in the open tab
        else:
            pass
            # Message about no project

    def _show_file_in_explorer(self, path):
        '''Iterate through the list of available projects and show
        the current file in the explorer view for the first
        project that contains it (i.e. if the same file is
        included in multiple open projects, the path will be
        expanded for the first project only).
        Note: This slot is connected to the main container's
        "showFileInExplorer(QString)" signal.'''
        central = IDE.get_service('central_container')
        if central and not central.is_lateral_panel_visible():
            return
        for project in self.projects:
            index = project.model().index(path)
            if index.isValid():
                # This highlights the index in the tree for us
                project.scrollTo(index, QAbstractItemView.EnsureVisible)
                project.setCurrentIndex(index)
                break

    def add_project(self, project):
        if project not in self.projects:
            self._combo_project.addItem(project.name)
            ptree = TreeProjectsWidget(project)
            self._projects_area.addWidget(ptree)
            ptree.closeProject.connect(self._close_project)
            print("project", project)#setRootPath
            pmodel = project.model
            ptree.setModel(pmodel)
            pindex = pmodel.index(pmodel.rootPath())
            ptree.setRootIndex(pindex)
            self.projects.append(ptree)
            current_index = self._projects_area.count()
            self._projects_area.setCurrentIndex(current_index - 1)
            self._combo_project.setCurrentIndex(current_index - 1)

    def _close_project(self, widget):
        """Close the project related to the tree widget."""
        index = self._projects_area.currentIndex()
        self.projects.remove(widget)
        self._projects_area.takeAt(index)
        self._combo_project.removeItem(index)
        index = self._combo_project.currentIndex()
        self._projects_area.setCurrentIndex(index)
        ninjaide = IDE.getInstance()
        ninjaide.filesystem.close_project(widget.project.path)
        widget.deleteLater()
        if len(self.projects) > 1:
            title = "%s (%s)" % (
                translations.TR_TAB_PROJECTS, len(self.projects))
        else:
            title = translations.TR_TAB_PROJECTS
        self.changeTitle.emit(self, title)

    def _change_current_project(self, index):
        self._projects_area.setCurrentIndex(index)

    def close_opened_projects(self):
        for project in reversed(self.projects):
            self._close_project(project)

    def save_project(self):
        """Save all the opened files that belongs to the actual project."""
        if self._projects_area.count() > 0:
            path = self.current_project.path
            main_container = IDE.get_service('main_container')
            if path and main_container:
                main_container.save_project(path)

    def create_new_project(self):
        wizard = new_project_manager.NewProjectManager(self)
        wizard.show()

    @property
    def current_project(self):
        if self._projects_area.count() > 0:
            return self._projects_area.currentWidget().project

    @property
    def current_tree(self):
        obj = self._projects_area.currentWidget()
        return obj

    def save_recent_projects(self, folder):
        settings = IDE.data_settings()
        recent_project_list = settings.value('recentProjects', {})
        #if already exist on the list update the date time
        projectProperties = json_manager.read_ninja_project(folder)
        name = projectProperties.get('name', '')
        description = projectProperties.get('description', '')

        if name == '':
            name = file_manager.get_basename(folder)

        if description == '':
            description = translations.TR_NO_DESCRIPTION

        if folder in recent_project_list:
            properties = recent_project_list[folder]
            properties["lastopen"] = QDateTime.currentDateTime()
            properties["name"] = name
            properties["description"] = description
            recent_project_list[folder] = properties
        else:
            recent_project_list[folder] = {
                "name": name,
                "description": description,
                "isFavorite": False, "lastopen": QDateTime.currentDateTime()}
            #if the length of the project list it's high that 10 then delete
            #the most old
            #TODO: add the length of available projects to setting
            if len(recent_project_list) > 10:
                del recent_project_list[self.find_most_old_open(
                    recent_project_list)]
        settings.setValue('recentProjects', recent_project_list)

    def find_most_old_open(self, recent_project_list):
        listFounder = []
        for recent_project_path, content in list(recent_project_list.items()):
            listFounder.append((recent_project_path, int(
                content["lastopen"].toString("yyyyMMddHHmmzzz"))))
        listFounder = sorted(listFounder, key=lambda date: listFounder[1],
                             reverse=True)   # sort by date last used
        return listFounder[0][0]

    def reject(self):
        if self.parent() is None:
            self.dockWidget.emit(self)

    def closeEvent(self, event):
        self.dockWidget.emit(self)
        event.ignore()

    def context_menu_for_root(self):
        if not self.current_tree:
            # en vez de un mensaje, colorear el area de projecto...
            # el area de projecto debería decir 'Sin Projectos'/'without Projects'
            # QMessageBox.information(self, translations.TR_INFO_TITLE_PROJECT_PROPERTIES,
            #     translations.TR_INFO_MESSAGE_PROJECT_PROPERTIES)
            return
        menu = QMenu(self)
        path = self.current_tree.project.path
        action_add_file = menu.addAction(QIcon(":img/new"),
                                         translations.TR_ADD_NEW_FILE)
        action_add_folder = menu.addAction(QIcon(
            ":img/openProj"), translations.TR_ADD_NEW_FOLDER)
        action_create_init = menu.addAction(translations.TR_CREATE_INIT)
        action_add_file.triggered['bool'].connect(lambda s,_path=path: self.current_tree._add_new_file(_path))
        action_add_folder.triggered['bool'].connect(lambda s,_path=path: self.current_tree._add_new_folder(_path))
        action_create_init.triggered['bool'].connect(lambda s,_path=path: self.current_tree._create_init(_path))
        menu.addSeparator()
        actionRunProject = menu.addAction(QIcon(
            ":img/play"), translations.TR_RUN_PROJECT)
        actionRunProject.triggered['bool'].connect(lambda s: self.current_tree._execute_project())
        if self.current_tree._added_to_console:
            actionRemoveFromConsole = menu.addAction(
                translations.TR_REMOVE_PROJECT_FROM_PYTHON_CONSOLE)
            actionRemoveFromConsole.triggered['bool'].connect(lambda s: self.current_tree._remove_project_from_console())
        else:
            actionAdd2Console = menu.addAction(
                translations.TR_ADD_PROJECT_TO_PYTHON_CONSOLE)
            actionAdd2Console.triggered['bool'].connect(lambda s: self.current_tree._add_project_to_console())
        actionShowFileSizeInfo = menu.addAction(translations.TR_SHOW_FILESIZE)
        actionShowFileSizeInfo.triggered['bool'].connect(lambda s: self.current_tree.show_filesize_info())
        actionProperties = menu.addAction(QIcon(":img/pref"),
                                          translations.TR_PROJECT_PROPERTIES)
        actionProperties.triggered['bool'].connect(lambda s: self.current_tree.open_project_properties())

        menu.addSeparator()
        action_close = menu.addAction(
            self.style().standardIcon(QStyle.SP_DialogCloseButton),
            translations.TR_CLOSE_PROJECT)
        action_close.triggered['bool'].connect(lambda s: self.current_tree._close_project())
        #menu for the project
        for m in self.current_tree.extra_menus_by_scope['project']:
            if isinstance(m, QMenu):
                menu.addSeparator()
                menu.addMenu(m)

        #show the menu!
        menu.exec_(QCursor.pos())

    def open_project_properties(self):
        print("open_project_properties-.-.-.-.-")
        _prj = self.current_project
        if _prj:
            proj = project_properties_widget.ProjectProperties(_prj, self)
            proj.show()
            return
        
        QMessageBox.information(self, translations.TR_INFO_TITLE_PROJECT_PROPERTIES,
                translations.TR_INFO_MESSAGE_PROJECT_PROPERTIES)
Esempio n. 46
0
class TaskWidget(QWidget):

    taskModified = pyqtSignal(tasklib.Task)
    taskDeleted = pyqtSignal(tasklib.Task)
    taskCreated = pyqtSignal(tasklib.Task)

    def __init__(self, parent, task, new=False):
        QWidget.__init__(self, parent)
        self.task = task
        self.new = new
        self.layout = QStackedLayout(self)

        self.label = QLabel(self)
        self.label.setTextFormat(Qt.RichText)
        self.label.setOpenExternalLinks(True)
        self._update()
        self.label.setWordWrap(True)
        self.layout.addWidget(self.label)

        self.editor = TaskEditorLineEdit(self, parent.mfile)
        self.layout.addWidget(self.editor)
        self.setLayout(self.layout)

        self.layout.setCurrentIndex(0)
        self.editor.focusOut.connect(self.editFinished)
        self.task.modified.connect(self._update)

    def sizeHint(self):
        new_height = self.label.heightForWidth(self.parent().size().width())
        new_height += 10
        return QSize(self.parent().size().width(), new_height)

    def edit(self):
        self.editor.setText(self.task.text)
        self.layout.setCurrentIndex(1)
        self.editor.setFocus()

    def editFinished(self):
        # qt bug, this method may be called several times for one edit
        self.layout.setCurrentIndex(0)
        text = self.editor.text()
        if not text:
            self.taskDeleted.emit(self.task)
        elif self.new:
            self.task.text = text
            self.new = False
            self.taskCreated.emit(self.task)
        elif text != self.task.text:
            self.task.text = text
            self.taskModified.emit(self)
        self.parent().setFocus()

    def _update(self):
        self.label.setText(self.task.toHtml())

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.editor.setText(self.task.text)
            self.layout.setCurrentIndex(0)
            self.parent().setFocus()
            return
        elif event.key() in (Qt.Key_Enter, Qt.Key_Return):
            self.editFinished()
            return
        QWidget.keyPressEvent(self, event)
Esempio n. 47
0
class Preferences(QDialog):

    configuration = {}
    weight = 0
    # Signals
    savePreferences = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent, Qt.Dialog)
        self.setWindowTitle(translations.TR_PREFERENCES_TITLE)
        self.setMinimumSize(900, 650)
        box = QVBoxLayout(self)
        box.setContentsMargins(3, 3, 3, 3)
        self.setAutoFillBackground(True)
        # Header
        self._header_label = QLabel("")
        header_font = self._header_label.font()
        header_font.setBold(True)
        header_font.setPointSize(header_font.pointSize() + 4)
        self._header_label.setFont(header_font)

        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)

        self.tree = QTreeWidget()
        self.tree.header().setHidden(True)
        self.tree.setSelectionMode(QTreeWidget.SingleSelection)
        self.tree.setAnimated(True)
        self.tree.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.tree.setFixedWidth(200)
        hbox.addWidget(self.tree)

        self.stacked = QStackedLayout()
        header_layout = QVBoxLayout()
        header_layout.setContentsMargins(0, 0, 0, 0)
        header_layout.addWidget(self._header_label)
        header_layout.addLayout(self.stacked)
        hbox.addLayout(header_layout)
        box.addLayout(hbox)

        # Footer buttons
        button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        box.addWidget(button_box)

        # Connections
        button_box.rejected.connect(self.close)
        button_box.accepted.connect(self._save_preferences)
        self.tree.selectionModel().currentRowChanged.connect(
            self._change_current)

        self.load_ui()

    @pyqtSlot()
    def _save_preferences(self):
        self.savePreferences.emit()
        self.close()

    def load_ui(self):
        sections = sorted(
            Preferences.configuration.keys(),
            key=lambda item: Preferences.configuration[item]['weight'])
        for section in sections:
            text = Preferences.configuration[section]['text']
            Widget = Preferences.configuration[section]['widget']
            widget = Widget(self)
            area = QScrollArea()
            area.setWidgetResizable(True)
            area.setWidget(widget)
            self.stacked.addWidget(area)
            index = self.stacked.indexOf(area)
            item = QTreeWidgetItem([text])
            item.setData(0, Qt.UserRole, index)
            self.tree.addTopLevelItem(item)

            # Sort Item Children
            subcontent = Preferences.configuration[section].get(
                'subsections', {})
            subsections = sorted(
                subcontent.keys(), key=lambda item: subcontent[item]['weight'])
            for sub in subsections:
                text = subcontent[sub]['text']
                Widget = subcontent[sub]['widget']
                widget = Widget(self)
                area = QScrollArea()
                area.setWidgetResizable(True)
                area.setWidget(widget)
                self.stacked.addWidget(area)
                index = self.stacked.indexOf(area)
                subitem = QTreeWidgetItem([text])
                subitem.setData(0, Qt.UserRole, index)
                item.addChild(subitem)

        self.tree.expandAll()
        self.tree.setCurrentIndex(self.tree.model().index(0, 0))

    def _change_current(self):
        item = self.tree.currentItem()
        index = item.data(0, Qt.UserRole)
        self.stacked.setCurrentIndex(index)
        self._header_label.setText(item.text(0))

    @classmethod
    def register_configuration(cls, section, widget, text,
                               weight=None, subsection=None):
        if weight is None:
            Preferences.weight += 1
            weight = Preferences.weight
        if subsection is None:
            Preferences.configuration[section] = {
                'widget': widget,
                'weight': weight,
                'text': text
            }
        else:
            config = Preferences.configuration.get(section, {})
            if not config:
                config[section] = {
                    'widget': None,
                    'weight': 100
                }
            subconfig = config.get('subsections', {})
            subconfig[subsection] = {
                'widget': widget,
                'weight': weight,
                'text': text
            }
            config['subsections'] = subconfig
            Preferences.configuration[section] = config
Esempio n. 48
0
class _MainContainer(QWidget):

    currentEditorChanged = pyqtSignal(str)
    fileOpened = pyqtSignal(str)
    beforeFileSaved = pyqtSignal(str)
    fileSaved = pyqtSignal(str)
    runFile = pyqtSignal(str)
    showFileInExplorer = pyqtSignal(str)
    addToProject = pyqtSignal(str)
    allFilesClosed = pyqtSignal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self._vbox = QVBoxLayout(self)
        self._vbox.setContentsMargins(0, 0, 0, 0)
        self._vbox.setSpacing(0)
        self.stack = QStackedLayout()
        self.stack.setStackingMode(QStackedLayout.StackAll)
        self._vbox.addLayout(self.stack)
        self.splitter = dynamic_splitter.DynamicSplitter()
        self._files_handler = files_handler.FilesHandler(self)

        # Code Navigation
        self.__code_back = []
        self.__code_forward = []
        self.__operations = {
            0: self._navigate_code_jumps,
            1: self._navigate_bookmarks
        }
        # Recent files list
        self.__last_opened_files = []
        # QML UI
        self._add_file_folder = add_file_folder.AddFileFolderWidget(self)

        if settings.SHOW_START_PAGE:
            self.show_start_page()

        IDE.register_service("main_container", self)
        # Register signals connections
        connections = (
            # {
            #     "target": "main_container",
            #     "signal_name": "updateLocator",
            #     "slot": self._explore_code
            # },
            {
                "target": "filesystem",
                "signal_name": "projectOpened",
                "slot": self._explore_code
            },
            # {
            #     "target": "projects_explore",
            #     "signal_name": "updateLocator",
            #     "slot": self._explore_code
            # }
            {
                "target": "filesystem",
                "signal_name": "projectClosed",
                "slot": self._explore_code
            }
        )

        IDE.register_signals("main_container", connections)

        fhandler_short = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_Tab), self)
        fhandler_short.activated.connect(self.show_files_handler)
        # Added for set language
        # self._setter_language = set_language.SetLanguageFile()

    def install(self):
        ninjaide = IDE.get_service("ide")
        ninjaide.place_me_on("main_container", self, "central", top=True)

        self.combo_area = combo_editor.ComboEditor(original=True)
        self.combo_area.allFilesClosed.connect(self._files_closed)
        self.combo_area.allFilesClosed.connect(
            lambda: self.allFilesClosed.emit())
        self.combo_area.fileClosed.connect(self._add_to_last_opened)
        self.splitter.add_widget(self.combo_area)
        self.add_widget(self.splitter)
        # self.current_widget = self.combo_area
        # Code Locator
        self._code_locator = locator_widget.LocatorWidget(ninjaide)

        data_settings = IDE.data_settings()
        recent_files = data_settings.value("lastSession/recentFiles")
        if recent_files is not None:
            self.__last_opened_files = recent_files
        ui_tools.install_shortcuts(self, actions.ACTIONS, ninjaide)
        # self.fileSaved.connect(self._show_message_about_saved)

    def run_file(self, filepath):
        self.runFile.emit(filepath)

    def _show_file_in_explorer(self, filepath):
        self.showFileInExplorer.emit(filepath)

    def _add_to_project(self, filepath):
        self.addToProject.emit(filepath)

    def show_files_handler(self):
        self._files_handler.next_item()

    def hide_files_handler(self):
        self._files_handler.hide()

    def import_from_everywhere(self):
        """Insert an import line from any place in the editor."""

        editorWidget = self.get_current_editor()
        if editorWidget:
            dialog = from_import_dialog.FromImportDialog(editorWidget, self)
            dialog.show()

    def navigate_code_history(self, operation, forward):
        self.__operations[operation](forward)

    def _navigate_code_jumps(self, forward=False):
        """Navigate between the jump points"""
        node = None
        if not forward and self.__code_back:
            if len(self.__code_back) == 1:
                return
            node = self.__code_back.pop()
            self.__code_forward.append(node)
            node = self.__code_back[-1]
        elif forward and self.__code_forward:
            node = self.__code_forward.pop()
            self.__code_back.append(node)
        if node is not None:
            filename = node[0]
            line, col = node[1]
            self.open_file(filename, line, col)

    def _navigate_bookmarks(self, forward=True):
        """Navigate between the bookmarks"""

        current_editor = self.get_current_editor()
        current_editor.navigate_bookmarks(forward=forward)

    def _set_focus_to_editor(self):
        status_bar = IDE.get_service("status_bar")
        tools_doock = IDE.get_service("tools_dock")
        editor_widget = self.get_current_editor()
        if status_bar.isVisible() and tools_doock.isVisible():
            status_bar.hide_status_bar()
        elif tools_doock.isVisible():
            tools_doock._hide()
        elif status_bar.isVisible():
            status_bar.hide_status_bar()
        if editor_widget is not None:
            editor_widget.extra_selections.remove("find")
            editor_widget.scrollbar().remove_marker("find")

    def split_assistance(self):
        editor_widget = self.get_current_editor()
        if editor_widget is not None:
            split_widget = split_orientation.SplitOrientation(self)
            split_widget.show()

    def show_dialog(self, widget):
        self.add_widget(widget)
        self.stack.setCurrentWidget(widget)

    def show_split(self, orientation_vertical=False):
        orientation = Qt.Horizontal
        if orientation_vertical:
            orientation = Qt.Vertical
        self.combo_area.split_editor(orientation)

    def show_locator(self):
        """Show the Locator Widget"""

        if not self._code_locator.isVisible():
            self._code_locator.show()

    def _explore_code(self):
        """Update locator metadata for the current projects"""

        self._code_locator.explore_code()

    def _explore_file_code(self, path):
        """Update locator metadata for the file in path"""

        self._code_locator.explore_file_code(path)

    def current_editor_changed(self, filename):
        """Notify the new filename of the current editor"""

        if filename is None:
            filename = translations.TR_NEW_DOCUMENT
        self.currentEditorChanged.emit(filename)

    def get_current_editor(self):
        current_widget = self.combo_area.current_editor()
        if isinstance(current_widget, editor.NEditor):
            return current_widget
        return None

    @property
    def last_opened_files(self):
        return self.__last_opened_files

    def _add_to_last_opened(self, nfile):
        MAX_RECENT_FILES = 10  # FIXME: configuration
        if nfile.is_new_file:
            return
        file_path = nfile.file_path
        if file_path in self.__last_opened_files:
            self.__last_opened_files.remove(file_path)
        self.__last_opened_files.insert(0, file_path)
        if len(self.__last_opened_files) > MAX_RECENT_FILES:
            self.__last_opened_files.pop(-1)

    def clear_last_opened_files(self):
        self.__last_opened_files.clear()

    def open_file(self, filename='', line=-1, col=0, ignore_checkers=False):
        if not filename:
            logger.debug("Has no filename")
            if settings.WORKSPACE:
                directory = settings.WORKSPACE
            else:
                directory = os.path.expanduser("~")
                editor_widget = self.get_current_editor()
                ninjaide = IDE.get_service("ide")
                current_project = ninjaide.get_current_project()
                # TODO: handle current project in NProject
                if current_project is not None:
                    directory = current_project.full_path
                elif editor_widget is not None and editor_widget.file_path:
                    directory = file_manager.get_folder(
                        editor_widget.file_path)
            filenames = QFileDialog.getOpenFileNames(
                self,
                translations.TR_OPEN_A_FILE,
                directory,
                settings.get_supported_extensions_filter(),
                initialFilter="Python files (*.py *.pyw)"
            )[0]
        else:
            logger.debug("Has filename")
            filenames = [filename]
        if not filenames:
            return
        for filename in filenames:
            image_extensions = ("png", "jpg", "jpeg", "bmp", "gif")
            if file_manager.get_file_extension(filename) in image_extensions:
                logger.debug("Will open as image")
                self.open_image(filename)
            else:
                logger.debug("Will try to open: %s" % filename)
                self.__open_file(
                    filename, line, col, ignore_checkers=ignore_checkers)

    def __open_file(self, filename, line, col, ignore_checkers=False):
        try:
            self.add_editor(filename)
            if line != -1:
                self.editor_go_to_line(line, col)
            self.currentEditorChanged.emit(filename)
        except file_manager.NinjaIOException as reason:
            QMessageBox.information(
                self,
                translations.TR_OPEN_FILE_ERROR,
                str(reason))
            logger.error("The file %s couldn't be open" % filename)

    def open_image(self, filename):
        for index in range(self.combo_area.stacked.count()):
            widget = self.combo_area.stacked.widget(index)
            if isinstance(widget, image_viewer.ImageViewer):
                if widget.image_filename == filename:
                    logger.debug("Image already open")
                    self.combo_area._set_current(neditable=None, index=index)
                    return
        viewer = image_viewer.ImageViewer(filename)
        self.combo_area.add_image_viewer(viewer)
        self.stack.setCurrentWidget(self.splitter)

    def autosave_file(self):
        for neditable in self.combo_area.bar.get_editables():
            neditable.autosave_file()

    def save_file(self, editor_widget=None):
        if editor_widget is None:
            # This may return None if there is not editor present
            editor_widget = self.get_current_editor()
        if editor_widget is None:
            return False
        # Ok, we have an editor instance
        # Save to file only if editor really was modified
        if editor_widget.is_modified:
            try:
                if editor_widget.nfile.is_new_file or \
                        not editor_widget.nfile.has_write_permission():
                    return self.save_file_as(editor_widget)

                file_path = editor_widget.file_path
                # Emit signal before save
                self.beforeFileSaved.emit(file_path)
                if settings.REMOVE_TRAILING_SPACES:
                    editor_widget.remove_trailing_spaces()
                if settings.ADD_NEW_LINE_AT_EOF:
                    editor_widget.insert_block_at_end()
                # Save content
                editor_widget.neditable.save_content()
                # FIXME: encoding
                message = translations.TR_FILE_SAVED.format(file_path)
                self.fileSaved.emit(message)
                return True
            except Exception as reason:
                logger.error("Save file error: %s" % reason)
                QMessageBox.information(
                    self,
                    translations.TR_SAVE_FILE_ERROR_TITLE,
                    translations.TR_SAVE_FILE_ERROR_BODY
                )
            return False

    def save_file_as(self, editor_widget=None):
        force = False
        if editor_widget is None:
            # We invoque from menu
            editor_widget = self.get_current_editor()
            if editor_widget is None:
                # We haven't editor in main container
                return False
            force = True
        try:
            filters = "(*.py);;(*.*)"
            if editor_widget.file_path is not None:  # Existing file
                extension = file_manager.get_file_extension(
                    editor_widget.file_path)
                if extension != 'py':
                    filters = "(*.%s);;(*.py);;(*.*)" % extension
                save_folder = self._get_save_folder(editor_widget.file_path)
            else:
                save_folder = settings.WORKSPACE

            filename = QFileDialog.getSaveFileName(
                self,
                translations.TR_SAVE_FILE_DIALOG,
                save_folder,
                filters
            )[0]
            if not filename:
                return False
            # FIXME: remove trailing spaces
            extension = file_manager.get_file_extension(filename)
            if not extension:
                filename = "%s.%s" % (filename, "py")
            editor_widget.neditable.save_content(path=filename, force=force)
            # self._setter_language.set_language_from_extension(extension)
            self.fileSaved.emit(translations.TR_FILE_SAVED.format(filename))
            self.currentEditorChanged.emit(filename)
            return True
        except file_manager.NinjaFileExistsException as reason:
            QMessageBox.information(
                self,
                translations.TR_FILE_ALREADY_EXISTS_TITLE,
                translations.TR_FILE_ALREADY_EXISTS_BODY.format(
                    reason.filename)
            )
        except Exception as reason:
            logger.error("Save file as: %s", reason)
            QMessageBox.information(
                self,
                translations.TR_SAVE_FILE_ERROR_TITLE,
                translations.TR_SAVE_FILE_ERROR_BODY
            )
        return False

    def save_project(self, project_path):
        """Save all files in the project path"""
        for neditable in self.combo_area.bar.get_editables():
            file_path = neditable.file_path
            if file_path is None:
                # FIXME: New edited files will not be saved, its ok?
                continue
            if file_manager.belongs_to_folder(project_path, file_path):
                neditable.save_content()

    def _get_save_folder(self, filename):
        """Returns the root directory of the 'Main Project'
        or the home folder"""

        ninjaide = IDE.get_service("ide")
        current_project = ninjaide.get_current_project()
        if current_project is not None:
            return current_project.path
        return os.path.expanduser("~")

    def close_file(self):
        self.combo_area.close_current_file()

    def add_editor(self, filename=None):
        ninjaide = IDE.get_service("ide")
        editable = ninjaide.get_or_create_editable(filename)

        if editable.editor:
            # If already open
            logger.debug("%s is already open" % filename)
            self.combo_area.set_current(editable)
            return self.combo_area.current_editor()
        else:
            pass

        editor_widget = self.create_editor_from_editable(editable)
        # editor_widget.set_language()
        # Add the tab
        keep_index = (self.splitter.count() > 1 and
                      self.combo_area.stacked.count() > 0)
        self.combo_area.add_editor(editable, keep_index)
        # Emit a signal about the file open
        self.fileOpened.emit(filename)

        if keep_index:
            self.combo_area.set_current(editable)

        self.stack.setCurrentWidget(self.splitter)
        editor_widget.setFocus()
        return editor_widget

    def create_editor_from_editable(self, editable):
        neditor = editor.create_editor(editable)
        neditor.zoomChanged.connect(self._on_zoom_changed)
        neditor.addBackItemNavigation.connect(self.add_back_item_navigation)
        editable.fileSaved.connect(
            lambda neditable: self._explore_file_code(neditable.file_path))
        return neditor

    def add_back_item_navigation(self):
        editor_widget = self.get_current_editor()
        if editor_widget is not None:
            item = (editor_widget.file_path, editor_widget.cursor_position)
            if item not in self.__code_back:
                self.__code_back.append(item)
                # self.__code_forward.clear()

    def _on_zoom_changed(self, zoom):
        text = "Zoom: {}%".format(str(zoom))
        ide = IDE.get_service("ide")
        ide.show_message(text)

    def add_widget(self, widget):
        self.stack.addWidget(widget)

    def show_start_page(self):
        """Show Start Page widget in main container"""

        startp = self.stack.widget(0)
        if isinstance(startp, start_page.StartPage):
            self.stack.setCurrentIndex(0)
        else:
            startp = start_page.StartPage(parent=self)
            startp.newFile.connect(self.add_editor)
            self.stack.insertWidget(0, startp)
            self.stack.setCurrentIndex(0)

    def _files_closed(self):
        if settings.SHOW_START_PAGE:
            self.show_start_page()

    def add_status_bar(self, status_bar):
        self._vbox.addWidget(status_bar)

    def create_file(self, base_path, project_path):
        self._add_file_folder.create_file(base_path, project_path)

    def create_folder(self, base_path, project_path):
        self._add_file_folder.create_folder(base_path, project_path)

    def restyle_editor(self):
        neditables = self.combo_area.bar.get_editables()
        for neditable in neditables:
            neditable.editor.restyle()

    def zoom_in_editor(self):
        """Increase the font size in the current editor"""

        editor_widget = self.get_current_editor()
        if editor_widget is not None:
            editor_widget.zoom(1.)

    def zoom_out_editor(self):
        """Decrease the font size in the current editor"""

        editor_widget = self.get_current_editor()
        if editor_widget is not None:
            editor_widget.zoom(-1.)

    def reset_zoom_editor(self):
        """Reset the to original font size in the current editor"""

        editor_widget = self.get_current_editor()
        if editor_widget is not None:
            editor_widget.reset_zoom()

    def editor_move_up(self):
        editor_widget = self.get_current_editor()
        if editor_widget is not None:
            editor_widget.move_up_down(up=True)

    def editor_move_down(self):
        editor_widget = self.get_current_editor()
        if editor_widget is not None:
            editor_widget.move_up_down()

    def editor_duplicate_line(self):
        editor_widget = self.get_current_editor()
        if editor_widget is not None:
            editor_widget.duplicate_line()

    def editor_toggle_comment(self):
        """Mark the current line or selection as a comment."""
        editor_widget = self.get_current_editor()
        if editor_widget is not None:
            editor_widget.comment_or_uncomment()

    def editor_go_to_line(self, line, column=0, center=True):
        editor_widget = self.get_current_editor()
        if editor_widget is not None:
            editor_widget.go_to_line(line, column, center)
            editor_widget.setFocus()

    def _editor_settings_changed(self, key, value):
        key = key.split("/")[-1]
        editor_widget = self.get_current_editor()
        if editor_widget is not None:
            callback = getattr(editor.NEditor, key, False)
            if callback:
                callback = callback
                if not hasattr(callback, "__call__"):
                    # Property!
                    callback = callback.fset
                callback(editor_widget, value)

    def toggle_tabs_and_spaces(self):
        """Toggle Show/Hide Tabs and Spaces"""

        settings.SHOW_TABS_AND_SPACES = not settings.SHOW_TABS_AND_SPACES
        qsettings = IDE.ninja_settings()
        qsettings.setValue('preferences/editor/showTabsAndSpaces',
                           settings.SHOW_TABS_AND_SPACES)
        neditor = self.get_current_editor()
        if neditor is not None:
            neditor.show_whitespaces = settings.SHOW_TABS_AND_SPACES

    def __navigate_with_keyboard(self, forward):
        """Navigate between the positions in the jump history stack."""
        operation = self.combo_area.bar.code_navigator.operation
        self.navigate_code_history(operation, forward)

    def navigate_back(self):
        self.__navigate_with_keyboard(forward=False)

    def navigate_forward(self):
        self.__navigate_with_keyboard(forward=True)
Esempio n. 49
0
class AppWindow(QMainWindow):
	"The application's main window"
	move_listener = pyqtSignal()
	db_activity_checker = pyqtSignal()
	graphics_blur = QGraphicsBlurEffect()
	def __init__(self):
		super().__init__()
		app_constants.GENERAL_THREAD = QThread(self)
		app_constants.GENERAL_THREAD.finished.connect(app_constants.GENERAL_THREAD.deleteLater)
		app_constants.GENERAL_THREAD.start()
		self.setAcceptDrops(True)
		self.initUI()
		self.start_up()
		QTimer.singleShot(3000, self._check_update)
		self.setFocusPolicy(Qt.NoFocus)
		self.set_shortcuts()
		self.graphics_blur.setParent(self)
		#ex = settings.ExProperties()
		#d = pewnet.ExHenManager(ex.ipb_id, ex.ipb_pass)
		#item = d.from_gallery_url('http://exhentai.org/g/861957/02741dc584/')
		#def a(): print(item.file)
		#if not item.file:
		#	item.file_rdy.connect(a)
		#else:
		#	a()

	def set_shortcuts(self):
		quit = QShortcut(QKeySequence('Ctrl+Q'), self, self.close)

	def init_watchers(self):

		def remove_gallery(g):
			index = self.manga_list_view.find_index(g.id, True)
			if index:
				self.manga_list_view.remove_gallery([index])

		def create_gallery(path):
			g_dia = gallerydialog.GalleryDialog(self, path)
			g_dia.SERIES.connect(self.manga_list_view.gallery_model.addRows)
			g_dia.show()

		def update_gallery(g):
			index = self.manga_list_view.find_index(g.id)
			if index:
				self.manga_list_view.replace_edit_gallery([g], index.row())
			else:
				log_e('Could not find gallery to update from watcher')

		def created(path):
			c_popup = io_misc.CreatedPopup(path, self)
			c_popup.ADD_SIGNAL.connect(create_gallery)
		def modified(path, gallery):
			mod_popup = io_misc.ModifiedPopup(path, gallery, self)
		def deleted(path, gallery):
			d_popup = io_misc.DeletedPopup(path, gallery, self)
			d_popup.UPDATE_SIGNAL.connect(update_gallery)
			d_popup.REMOVE_SIGNAL.connect(remove_gallery)
		def moved(new_path, gallery):
			mov_popup = io_misc.MovedPopup(new_path, gallery, self)
			mov_popup.UPDATE_SIGNAL.connect(update_gallery)

		self.watchers = io_misc.Watchers()
		self.watchers.gallery_handler.CREATE_SIGNAL.connect(created)
		self.watchers.gallery_handler.MODIFIED_SIGNAL.connect(modified)
		self.watchers.gallery_handler.MOVED_SIGNAL.connect(moved)
		self.watchers.gallery_handler.DELETED_SIGNAL.connect(deleted)

	admin_db_method_invoker = pyqtSignal(str)
	def start_up(self):
		hello = ["Hello!", "Hi!", "Onii-chan!", "Senpai!", "Hisashiburi!", "Welcome!", "Okaerinasai!", "Welcome back!", "Hajimemashite!"]
		self.notification_bar.add_text("{} Please don't hesitate to report any bugs you find.".format(hello[random.randint(0, len(hello)-1)])+
								 " Go to Settings -> About -> Bug Reporting for more info!")
		level = 5
		def normalize_first_time():
			settings.set(level, 'Application', 'first time level')
			settings.save()

		def done(status=True):
			gallerydb.DatabaseEmitter.RUN = True
			if app_constants.FIRST_TIME_LEVEL != level:
				normalize_first_time()
			if app_constants.ENABLE_MONITOR and\
				app_constants.MONITOR_PATHS and all(app_constants.MONITOR_PATHS):
				self.init_watchers()
				if app_constants.LOOK_NEW_GALLERY_STARTUP:
					if self.manga_list_view.gallery_model.db_emitter.count == app_constants.GALLERY_DATA:
						self.scan_for_new_galleries()
					else:
						self.manga_list_view.gallery_model.db_emitter.DONE.connect(self.scan_for_new_galleries)
			self.download_manager = pewnet.Downloader()
			app_constants.DOWNLOAD_MANAGER = self.download_manager
			self.download_manager.start_manager(4)

		if app_constants.FIRST_TIME_LEVEL < 4:
			log_i('Invoking first time level {}'.format(4))
			settings.set([], 'Application', 'monitor paths')
			settings.set([], 'Application', 'ignore paths')
			app_constants.MONITOR_PATHS = []
			app_constants.IGNORE_PATHS = []
			settings.save()
			done()
		elif app_constants.FIRST_TIME_LEVEL < 5:
			log_i('Invoking first time level {}'.format(5))
			app_widget = misc.ApplicationPopup(self)
			app_widget.note_info.setText("<font color='red'>IMPORTANT:</font> Application restart is required when done")
			app_widget.restart_info.hide()
			self.admin_db = gallerydb.AdminDB()
			self.admin_db.moveToThread(app_constants.GENERAL_THREAD)
			self.admin_db.DONE.connect(done)
			self.admin_db.DONE.connect(lambda: app_constants.NOTIF_BAR.add_text("Application requires a restart"))
			self.admin_db.DONE.connect(self.admin_db.deleteLater)
			self.admin_db.DATA_COUNT.connect(app_widget.prog.setMaximum)
			self.admin_db.PROGRESS.connect(app_widget.prog.setValue)
			self.admin_db_method_invoker.connect(self.admin_db.rebuild_db)
			self.admin_db_method_invoker.connect(app_widget.show)
			app_widget.adjustSize()
			db_p = os.path.join(os.path.split(database.db_constants.DB_PATH)[0], 'sadpanda.db')
			self.admin_db_method_invoker.emit(db_p)
		else:
			done()

	def initUI(self):
		self.center = QWidget()
		self.display = QStackedLayout()
		self._main_layout = QVBoxLayout()
		self._main_layout.setSpacing(0)
		self._main_layout.setContentsMargins(0,0,0,0)
		self._main_layout.addLayout(self.display)
		self.center.setLayout(self._main_layout)
		# init the manga view variables
		self.manga_display()
		log_d('Create manga display: OK')
		# init the chapter view variables
		#self.chapter_display()
		self.m_l_view_index = self.display.addWidget(self.manga_list_view)
		self.m_t_view_index = self.display.addWidget(self.manga_table_view)
		self.download_window = io_misc.GalleryDownloader(self)
		self.download_window.hide()
		# init toolbar
		self.init_toolbar()
		log_d('Create toolbar: OK')
		# init status bar
		self.init_stat_bar()
		log_d('Create statusbar: OK')

		self.tags_treeview = None
		if app_constants.TAGS_TREEVIEW_ON_START:
			def tags_tree_none(): self.tags_treeview = None
			self.tags_treeview = misc_db.DBOverview(self, True)
			self.tags_treeview.about_to_close.connect(tags_tree_none)
			self.tags_treeview.show()

		self.system_tray = misc.SystemTray(QIcon(app_constants.APP_ICO_PATH), self)
		app_constants.SYSTEM_TRAY = self.system_tray
		tray_menu = QMenu(self)
		self.system_tray.setContextMenu(tray_menu)
		self.system_tray.setToolTip('Happypanda {}'.format(app_constants.vs))
		tray_quit = QAction('Quit', tray_menu)
		tray_update = tray_menu.addAction('Check for update')
		tray_update.triggered.connect(self._check_update)
		tray_menu.addAction(tray_quit)
		tray_quit.triggered.connect(self.close)
		self.system_tray.show()
		def tray_activate(r=None):
			if not r or r == QSystemTrayIcon.Trigger:
				self.showNormal()
				self.activateWindow()
		self.system_tray.messageClicked.connect(tray_activate)
		self.system_tray.activated.connect(tray_activate)
		log_d('Create system tray: OK')
		#self.display.addWidget(self.chapter_main)

		self.setCentralWidget(self.center)
		self.setWindowIcon(QIcon(app_constants.APP_ICO_PATH))


		props = settings.win_read(self, 'AppWindow')
		if props.resize:
			x, y = props.resize
			self.resize(x, y)
		else:
			self.resize(app_constants.MAIN_W, app_constants.MAIN_H)
		posx, posy = props.pos
		self.move(posx, posy)
		self.init_spinners()
		self.show()
		log_d('Show window: OK')

		self.notification_bar = misc.NotificationOverlay(self)
		p = self.toolbar.pos()
		self.notification_bar.move(p.x(), p.y()+self.toolbar.height())
		self.notification_bar.resize(self.width())
		app_constants.NOTIF_BAR = self.notification_bar
		log_d('Create notificationbar: OK')

		log_d('Window Create: OK')

	def _check_update(self):
		class upd_chk(QObject):
			UPDATE_CHECK = pyqtSignal(str)
			def __init__(self, **kwargs):
				super().__init__(**kwargs)
			def fetch_vs(self):
				import requests
				import time
				log_d('Checking Update')
				time.sleep(1.5)
				try:
					if os.path.exists('cacert.pem'):
						r = requests.get("https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt",
							  verify='cacert.pem')
					else:
						r = requests.get("https://raw.githubusercontent.com/Pewpews/happypanda/master/VS.txt")
					a = r.text
					vs = a.strip()
					self.UPDATE_CHECK.emit(vs)
				except:
					log.exception('Checking Update: FAIL')
					self.UPDATE_CHECK.emit('this is a very long text which is sure to be over limit')

		def check_update(vs):
			log_i('Received version: {}\nCurrent version: {}'.format(vs, app_constants.vs))
			if vs != app_constants.vs:
				if len(vs) < 10:
					self.notification_bar.begin_show()
					self.notification_bar.add_text("Version {} of Happypanda is".format(vs)+
									   " available. Click here to update!", False)
					self.notification_bar.clicked.connect(lambda: utils.open_web_link(
						'https://github.com/Pewpews/happypanda/releases'))
					self.notification_bar.set_clickable(True)
				else:
					self.notification_bar.add_text("An error occurred while checking for new version")

		self.update_instance = upd_chk()
		thread = QThread(self)
		self.update_instance.moveToThread(thread)
		thread.started.connect(self.update_instance.fetch_vs)
		self.update_instance.UPDATE_CHECK.connect(check_update)
		self.update_instance.UPDATE_CHECK.connect(self.update_instance.deleteLater)
		thread.finished.connect(thread.deleteLater)
		thread.start()

	def _web_metadata_picker(self, gallery, title_url_list, queue, parent=None):
		if not parent:
			parent = self
		text = "Which gallery do you want to extract metadata from?"
		s_gallery_popup = misc.SingleGalleryChoices(gallery, title_url_list,
											  text, parent)
		s_gallery_popup.USER_CHOICE.connect(queue.put)

	def get_metadata(self, gal=None):
		metadata_spinner = misc.Spinner(self)
		def move_md_spinner():
			metadata_spinner.update_move(
			QPoint(
				self.pos().x()+self.width()-65,
				self.pos().y()+self.toolbar.height()+55))
		metadata_spinner.set_text("Metadata")
		metadata_spinner.set_size(55)
		metadata_spinner.move(QPoint(self.pos().x()+self.width()-65,
							   self.pos().y()+self.toolbar.height()+55))
		self.move_listener.connect(move_md_spinner)
		thread = QThread(self)
		thread.setObjectName('App.get_metadata')
		fetch_instance = fetch.Fetch()
		if gal:
			if not isinstance(gal, list):
				galleries = [gal]
			else:
				galleries = gal
		else:
			if app_constants.CONTINUE_AUTO_METADATA_FETCHER:
				galleries = [g for g in self.manga_list_view.gallery_model._data if not g.exed]
			else:
				galleries = self.manga_list_view.gallery_model._data
			if not galleries:
				self.notification_bar.add_text('Looks like we\'ve already gone through all galleries!')
				return None
		fetch_instance.galleries = galleries

		self.notification_bar.begin_show()
		fetch_instance.moveToThread(thread)

		def done(status):
			self.notification_bar.end_show()
			fetch_instance.deleteLater()
			if not isinstance(status, bool):
				galleries = []
				for tup in status:
					galleries.append(tup[0])

				class GalleryContextMenu(QMenu):
					app_instance = self
					def __init__(self, parent=None):
						super().__init__(parent)
						show_in_library_act = self.addAction('Show in library')
						show_in_library_act.triggered.connect(self.show_in_library)

					def show_in_library(self):
						viewer = self.app_instance.manga_list_view
						index = viewer.find_index(self.gallery_widget.gallery.id, True)
						if index:
							self.app_instance.manga_table_view.scroll_to_index(index)
							self.app_instance.manga_list_view.scroll_to_index(index)

				g_popup = io_misc.GalleryPopup(('Fecthing metadata for these galleries failed.'+
									  ' Check happypanda.log for details.', galleries), self, menu=GalleryContextMenu())
				#errors = {g[0].id: g[1] for g in status}
				#for g_item in g_popup.get_all_items():
				#	g_item.setToolTip(errors[g_item.gallery.id])
				g_popup.graphics_blur.setEnabled(False)
				close_button = g_popup.add_buttons('Close')[0]
				close_button.clicked.connect(g_popup.close)

		fetch_instance.GALLERY_PICKER.connect(self._web_metadata_picker)
		fetch_instance.GALLERY_EMITTER.connect(self.manga_list_view.replace_edit_gallery)
		fetch_instance.AUTO_METADATA_PROGRESS.connect(self.notification_bar.add_text)
		thread.started.connect(fetch_instance.auto_web_metadata)
		fetch_instance.FINISHED.connect(done)
		fetch_instance.FINISHED.connect(metadata_spinner.close)
		fetch_instance.FINISHED.connect(lambda: self.move_listener.disconnect(move_md_spinner))
		thread.finished.connect(thread.deleteLater)
		thread.start()
		metadata_spinner.show()

	def init_stat_bar(self):
		self.status_bar = self.statusBar()
		self.status_bar.setMaximumHeight(20)
		self.status_bar.setSizeGripEnabled(False)
		self.stat_info = QLabel()
		self.stat_info.setIndent(5)
		self.sort_main = QAction("Asc", self)
		sort_menu = QMenu()
		self.sort_main.setMenu(sort_menu)
		s_by_title = QAction("Title", sort_menu)
		s_by_artist = QAction("Artist", sort_menu)
		sort_menu.addAction(s_by_title)
		sort_menu.addAction(s_by_artist)
		self.status_bar.addPermanentWidget(self.stat_info)
		#self.status_bar.addAction(self.sort_main)
		self.temp_msg = QLabel()
		self.temp_timer = QTimer()

		self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.connect(self.stat_row_info)
		self.manga_list_view.gallery_model.db_emitter.COUNT_CHANGE.connect(self.stat_row_info)
		self.manga_list_view.gallery_model.STATUSBAR_MSG.connect(self.stat_temp_msg)
		self.manga_list_view.STATUS_BAR_MSG.connect(self.stat_temp_msg)
		self.manga_table_view.STATUS_BAR_MSG.connect(self.stat_temp_msg)
		self.stat_row_info()
		app_constants.STAT_MSG_METHOD = self.stat_temp_msg

	def stat_temp_msg(self, msg):
		self.temp_timer.stop()
		self.temp_msg.setText(msg)
		self.status_bar.addWidget(self.temp_msg)
		self.temp_timer.timeout.connect(self.temp_msg.clear)
		self.temp_timer.setSingleShot(True)
		self.temp_timer.start(5000)

	def stat_row_info(self):
		r = self.manga_list_view.model().rowCount()
		t = self.manga_list_view.gallery_model.db_emitter.count
		self.stat_info.setText("Loaded {} of {} ".format(r, t))

	def manga_display(self):
		"initiates the manga view and related things"
		#list view
		self.manga_list_view = gallery.MangaView(self)

		#table view

		self.manga_table_view = gallery.MangaTableView(self)
		self.manga_table_view.gallery_model = self.manga_list_view.gallery_model
		self.manga_table_view.sort_model = self.manga_list_view.sort_model
		self.manga_table_view.setModel(self.manga_table_view.sort_model)
		self.manga_table_view.sort_model.change_model(self.manga_table_view.gallery_model)
		self.manga_table_view.setColumnWidth(app_constants.FAV, 20)
		self.manga_table_view.setColumnWidth(app_constants.ARTIST, 200)
		self.manga_table_view.setColumnWidth(app_constants.TITLE, 400)
		self.manga_table_view.setColumnWidth(app_constants.TAGS, 300)
		self.manga_table_view.setColumnWidth(app_constants.TYPE, 60)
		self.manga_table_view.setColumnWidth(app_constants.CHAPTERS, 60)
		self.manga_table_view.setColumnWidth(app_constants.LANGUAGE, 100)
		self.manga_table_view.setColumnWidth(app_constants.LINK, 400)


	def init_spinners(self):
		# fetching spinner
		self.data_fetch_spinner = misc.Spinner(self)
		self.data_fetch_spinner.set_size(60)
		self.move_listener.connect(
			lambda: self.data_fetch_spinner.update_move(
				QPoint(self.pos().x()+self.width()//2, self.pos().y()+self.height()//2)))
		
		self.manga_list_view.gallery_model.ADD_MORE.connect(self.data_fetch_spinner.show)
		self.manga_list_view.gallery_model.db_emitter.START.connect(self.data_fetch_spinner.show)
		self.manga_list_view.gallery_model.ADDED_ROWS.connect(self.data_fetch_spinner.before_hide)
		self.manga_list_view.gallery_model.db_emitter.CANNOT_FETCH_MORE.connect(self.data_fetch_spinner.before_hide)

		## deleting spinner
		#self.gallery_delete_spinner = misc.Spinner(self)
		#self.gallery_delete_spinner.set_size(40,40)
		##self.gallery_delete_spinner.set_text('Removing...')
		#self.manga_list_view.gallery_model.rowsAboutToBeRemoved.connect(self.gallery_delete_spinner.show)
		#self.manga_list_view.gallery_model.rowsRemoved.connect(self.gallery_delete_spinner.before_hide)


	def search(self, srch_string):
		self.search_bar.setText(srch_string)
		self.search_backward.setVisible(True)
		self.manga_list_view.sort_model.init_search(srch_string)
		old_cursor_pos = self._search_cursor_pos[0]
		self.search_bar.end(False)
		if self.search_bar.cursorPosition() != old_cursor_pos+1:
			self.search_bar.setCursorPosition(old_cursor_pos)


	def favourite_display(self):
		"Switches to favourite display"
		self.manga_table_view.sort_model.fav_view()
		self.favourite_btn.selected = True
		self.library_btn.selected = False

	def catalog_display(self):
		"Switches to catalog display"
		self.manga_table_view.sort_model.catalog_view()
		self.library_btn.selected = True
		self.favourite_btn.selected = False

	def settings(self):
		sett = settingsdialog.SettingsDialog(self)
		sett.scroll_speed_changed.connect(self.manga_list_view.updateGeometries)
		#sett.show()

	def init_toolbar(self):
		self.toolbar = QToolBar()
		self.toolbar.setFixedHeight(25)
		self.toolbar.setWindowTitle("Show") # text for the contextmenu
		#self.toolbar.setStyleSheet("QToolBar {border:0px}") # make it user defined?
		self.toolbar.setMovable(False)
		self.toolbar.setFloatable(False)
		#self.toolbar.setIconSize(QSize(20,20))
		self.toolbar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
		self.toolbar.setIconSize(QSize(20,20))

		spacer_start = QWidget() # aligns the first actions properly
		spacer_start.setFixedSize(QSize(10, 1))
		self.toolbar.addWidget(spacer_start)

		self.favourite_btn = misc.ToolbarButton(self.toolbar, 'Favorites')
		self.toolbar.addWidget(self.favourite_btn)
		self.favourite_btn.clicked.connect(self.favourite_display) #need lambda to pass extra args

		self.library_btn = misc.ToolbarButton(self.toolbar, 'Library')
		self.toolbar.addWidget(self.library_btn)
		self.library_btn.clicked.connect(self.catalog_display) #need lambda to pass extra args
		self.library_btn.selected = True

		self.toolbar.addSeparator()

		gallery_menu = QMenu()
		gallery_action = QToolButton()
		gallery_action.setText('Gallery ')
		gallery_action.setPopupMode(QToolButton.InstantPopup)
		gallery_action.setToolTip('Contains various gallery related features')
		gallery_action.setMenu(gallery_menu)
		add_gallery_icon = QIcon(app_constants.PLUS_PATH)
		gallery_action_add = QAction(add_gallery_icon, "Add single gallery...", self)
		gallery_action_add.triggered.connect(self.manga_list_view.SERIES_DIALOG.emit)
		gallery_action_add.setToolTip('Add a single gallery thoroughly')
		gallery_menu.addAction(gallery_action_add)
		add_more_action = QAction(add_gallery_icon, "Add galleries...", self)
		add_more_action.setStatusTip('Add galleries from different folders')
		add_more_action.triggered.connect(lambda: self.populate(True))
		gallery_menu.addAction(add_more_action)
		populate_action = QAction(add_gallery_icon, "Populate from directory/archive...", self)
		populate_action.setStatusTip('Populates the DB with galleries from a single folder or archive')
		populate_action.triggered.connect(self.populate)
		gallery_menu.addAction(populate_action)
		gallery_menu.addSeparator()
		metadata_action = QAction('Get metadata for all galleries', self)
		metadata_action.triggered.connect(self.get_metadata)
		gallery_menu.addAction(metadata_action)
		scan_galleries_action = QAction('Scan for new galleries', self)
		scan_galleries_action.triggered.connect(self.scan_for_new_galleries)
		scan_galleries_action.setStatusTip('Scan monitored folders for new galleries')
		gallery_menu.addAction(scan_galleries_action)
		gallery_action_random = gallery_menu.addAction("Open random gallery")
		gallery_action_random.triggered.connect(self.manga_list_view.open_random_gallery)
		self.toolbar.addWidget(gallery_action)


		misc_action = QToolButton()
		misc_action.setText('Tools ')
		misc_action_menu = QMenu()
		misc_action.setMenu(misc_action_menu)
		misc_action.setPopupMode(QToolButton.InstantPopup)
		misc_action.setToolTip("Contains misc. features")
		gallery_downloader = QAction("Gallery Downloader", misc_action_menu)
		gallery_downloader.triggered.connect(self.download_window.show)
		misc_action_menu.addAction(gallery_downloader)
		duplicate_check_simple = QAction("Simple Duplicate Finder", misc_action_menu)
		duplicate_check_simple.triggered.connect(lambda: self.manga_list_view.duplicate_check())
		misc_action_menu.addAction(duplicate_check_simple)
		self.toolbar.addWidget(misc_action)

		spacer_middle = QWidget() # aligns buttons to the right
		spacer_middle.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
		self.toolbar.addWidget(spacer_middle)


		sort_action = QToolButton()
		sort_action.setIcon(QIcon(app_constants.SORT_PATH))
		sort_action.setMenu(misc.SortMenu(self.toolbar, self.manga_list_view))
		sort_action.setPopupMode(QToolButton.InstantPopup)
		self.toolbar.addWidget(sort_action)
		
		self.grid_toggle_g_icon = QIcon(app_constants.GRID_PATH)
		self.grid_toggle_l_icon = QIcon(app_constants.LIST_PATH)
		self.grid_toggle = QToolButton()
		if self.display.currentIndex() == self.m_l_view_index:
			self.grid_toggle.setIcon(self.grid_toggle_l_icon)
		else:
			self.grid_toggle.setIcon(self.grid_toggle_g_icon)
		self.grid_toggle.setObjectName('gridtoggle')
		self.grid_toggle.clicked.connect(self.toggle_view)
		self.toolbar.addWidget(self.grid_toggle)

		spacer_mid2 = QWidget()
		spacer_mid2.setFixedSize(QSize(5, 1))
		self.toolbar.addWidget(spacer_mid2)

		def set_search_case(b):
			app_constants.GALLERY_SEARCH_CASE = b
			settings.set(b, 'Application', 'gallery search case')
			settings.save()

		def set_search_strict(b):
			app_constants.GALLERY_SEARCH_STRICT = b
			settings.set(b, 'Application', 'gallery search strict')
			settings.save()

		self.search_bar = misc.LineEdit()
		search_options = self.search_bar.addAction(QIcon(app_constants.SEARCH_OPTIONS_PATH), QLineEdit.TrailingPosition)
		search_options_menu = QMenu(self)
		search_options.triggered.connect(lambda: search_options_menu.popup(QCursor.pos()))
		search_options.setMenu(search_options_menu)
		case_search_option = search_options_menu.addAction('Case Sensitive')
		case_search_option.setCheckable(True)
		case_search_option.setChecked(app_constants.GALLERY_SEARCH_CASE)
		case_search_option.toggled.connect(set_search_case)
		strict_search_option = search_options_menu.addAction('Match whole terms')
		strict_search_option.setCheckable(True)
		strict_search_option.setChecked(app_constants.GALLERY_SEARCH_STRICT)
		strict_search_option.toggled.connect(set_search_strict)
		self.search_bar.setObjectName('search_bar')
		self.search_timer = QTimer(self)
		self.search_timer.setSingleShot(True)
		self.search_timer.timeout.connect(lambda: self.search(self.search_bar.text()))
		self._search_cursor_pos = [0, 0]
		def set_cursor_pos(old, new):
			self._search_cursor_pos[0] = old
			self._search_cursor_pos[1] = new
		self.search_bar.cursorPositionChanged.connect(set_cursor_pos)

		if app_constants.SEARCH_AUTOCOMPLETE:
			completer = QCompleter(self)
			completer_view = misc.CompleterPopupView()
			completer.setPopup(completer_view)
			completer_view._setup()
			completer.setModel(self.manga_list_view.gallery_model)
			completer.setCaseSensitivity(Qt.CaseInsensitive)
			completer.setCompletionMode(QCompleter.PopupCompletion)
			completer.setCompletionRole(Qt.DisplayRole)
			completer.setCompletionColumn(app_constants.TITLE)
			completer.setFilterMode(Qt.MatchContains)
			self.search_bar.setCompleter(completer)
			self.search_bar.returnPressed.connect(lambda: self.search(self.search_bar.text()))
		if not app_constants.SEARCH_ON_ENTER:
			self.search_bar.textEdited.connect(lambda: self.search_timer.start(800))
		self.search_bar.setPlaceholderText("Search title, artist, namespace & tags")
		self.search_bar.setMinimumWidth(150)
		self.search_bar.setMaximumWidth(500)
		self.search_bar.setFixedHeight(19)
		self.manga_list_view.sort_model.HISTORY_SEARCH_TERM.connect(lambda a: self.search_bar.setText(a))
		self.toolbar.addWidget(self.search_bar)

		def search_history(_, back=True): # clicked signal passes a bool
			sort_model =  self.manga_list_view.sort_model
			nav = sort_model.PREV if back else sort_model.NEXT
			history_term = sort_model.navigate_history(nav)
			if back:
				self.search_forward.setVisible(True)

		back = QShortcut(QKeySequence(QKeySequence.Back), self, lambda: search_history(None))
		forward = QShortcut(QKeySequence(QKeySequence.Forward), self, lambda: search_history(None, False))

		search_backbutton = QToolButton(self.toolbar)
		search_backbutton.setText(u'\u25C0')
		search_backbutton.setFixedWidth(15)
		search_backbutton.clicked.connect(search_history)
		self.search_backward = self.toolbar.addWidget(search_backbutton)
		self.search_backward.setVisible(False)
		search_forwardbutton = QToolButton(self.toolbar)
		search_forwardbutton.setText(u'\u25B6')
		search_forwardbutton.setFixedWidth(15)
		search_forwardbutton.clicked.connect(lambda: search_history(None, False))
		self.search_forward = self.toolbar.addWidget(search_forwardbutton)
		self.search_forward.setVisible(False)

		spacer_end = QWidget() # aligns settings action properly
		spacer_end.setFixedSize(QSize(10, 1))
		self.toolbar.addWidget(spacer_end)

		settings_act = QToolButton(self.toolbar)
		settings_act.setIcon(QIcon(app_constants.SETTINGS_PATH))
		settings_act.clicked.connect(self.settings)
		self.toolbar.addWidget(settings_act)

		spacer_end2 = QWidget() # aligns About action properly
		spacer_end2.setFixedSize(QSize(5, 1))
		self.toolbar.addWidget(spacer_end2)
		self.addToolBar(self.toolbar)

	def toggle_view(self):
		"""
		Toggles the current display view
		"""
		if self.display.currentIndex() == self.m_l_view_index:
			self.display.setCurrentIndex(self.m_t_view_index)
			self.grid_toggle.setIcon(self.grid_toggle_g_icon)
		else:
			self.display.setCurrentIndex(self.m_l_view_index)
			self.grid_toggle.setIcon(self.grid_toggle_l_icon)

	# TODO: Improve this so that it adds to the gallery dialog,
	# so user can edit data before inserting (make it a choice)
	def populate(self, mixed=None):
		"Populates the database with gallery from local drive'"
		if mixed:
			gallery_view = misc.GalleryListView(self, True)
			gallery_view.SERIES.connect(self.gallery_populate)
			gallery_view.show()
		else:
			msg_box = misc.BasePopup(self)
			l = QVBoxLayout()
			msg_box.main_widget.setLayout(l)
			l.addWidget(QLabel('Directory or Archive?'))
			l.addLayout(msg_box.buttons_layout)

			def from_dir():
				path = QFileDialog.getExistingDirectory(self, "Choose a directory containing your galleries")
				if not path:
					return
				msg_box.close()
				app_constants.OVERRIDE_SUBFOLDER_AS_GALLERY = True
				self.gallery_populate(path, True)
			def from_arch():
				path = QFileDialog.getOpenFileName(self, 'Choose an archive containing your galleries',
									   filter=utils.FILE_FILTER)
				path = [path[0]]
				if not all(path) or not path:
					return
				msg_box.close()
				app_constants.OVERRIDE_SUBFOLDER_AS_GALLERY = True
				self.gallery_populate(path, True)

			buttons = msg_box.add_buttons('Directory', 'Archive', 'Close')
			buttons[2].clicked.connect(msg_box.close)
			buttons[0].clicked.connect(from_dir)
			buttons[1].clicked.connect(from_arch)
			msg_box.adjustSize()
			msg_box.show()

	def gallery_populate(self, path, validate=False):
		"Scans the given path for gallery to add into the DB"
		if len(path) is not 0:
			data_thread = QThread(self)
			data_thread.setObjectName('General gallery populate')
			loading = misc.Loading(self)
			self.g_populate_inst = fetch.Fetch()
			self.g_populate_inst.series_path = path
			loading.show()

			def finished(status):
				def hide_loading():
					loading.hide()
				if status:
					if len(status) != 0:
						def add_gallery(gallery_list):
							def append_to_model(x):
								self.manga_list_view.sort_model.insertRows(x, None, len(x))
								self.manga_list_view.sort_model.init_search(
									self.manga_list_view.sort_model.current_term)

							class A(QObject):
								done = pyqtSignal()
								prog = pyqtSignal(int)
								def __init__(self, obj, parent=None):
									super().__init__(parent)
									self.obj = obj
									self.galleries = []

								def add_to_db(self):
									for y, x in enumerate(self.obj):
										gallerydb.add_method_queue(
											gallerydb.GalleryDB.add_gallery_return, False, x)
										self.galleries.append(x)
										y += 1
										self.prog.emit(y)
									append_to_model(self.galleries)
									self.done.emit()
							loading.progress.setMaximum(len(gallery_list))
							self.a_instance = A(gallery_list)
							thread = QThread(self)
							thread.setObjectName('Database populate')
							def loading_show(numb):
								if loading.isHidden():
									loading.show()
								loading.setText('Populating database ({}/{})\nPlease wait...'.format(
									numb, loading.progress.maximum()))
								loading.progress.setValue(numb)
								loading.show()

							def loading_hide():
								loading.close()
								self.manga_list_view.gallery_model.ROWCOUNT_CHANGE.emit()

							self.a_instance.moveToThread(thread)
							self.a_instance.prog.connect(loading_show)
							thread.started.connect(self.a_instance.add_to_db)
							self.a_instance.done.connect(loading_hide)
							self.a_instance.done.connect(self.a_instance.deleteLater)
							#a_instance.add_to_db()
							thread.finished.connect(thread.deleteLater)
							thread.start()
						#data_thread.quit
						hide_loading()
						log_i('Populating DB from gallery folder: OK')
						if validate:
							gallery_list = misc.GalleryListView(self)
							gallery_list.SERIES.connect(add_gallery)
							for ser in status:
								if ser.is_archive and app_constants.SUBFOLDER_AS_GALLERY:
									p = os.path.split(ser.path)[1]
									if ser.chapters[0].path:
										pt_in_arch = os.path.split(ser.path_in_archive)
										pt_in_arch = pt_in_arch[1] or pt_in_arch[0]
										text = '{}: {}'.format(p, pt_in_arch)
									else:
										text = p
									gallery_list.add_gallery(ser, text)
								else:
									gallery_list.add_gallery(ser, os.path.split(ser.path)[1])
							#self.manga_list_view.gallery_model.populate_data()
							gallery_list.update_count()
							gallery_list.show()
						else:
							add_gallery(status)
					else:
						log_d('No new gallery was found')
						loading.setText("No new gallery found")
						#data_thread.quit

				else:
					log_e('Populating DB from gallery folder: Nothing was added!')
					loading.setText("<font color=red>Nothing was added. Check happypanda_log for details..</font>")
					loading.progress.setStyleSheet("background-color:red;")
					data_thread.quit
					QTimer.singleShot(8000, loading.close)

			def skipped_gs(s_list):
				"Skipped galleries"
				msg_box = QMessageBox(self)
				msg_box.setIcon(QMessageBox.Question)
				msg_box.setText('Do you want to view skipped paths?')
				msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
				msg_box.setDefaultButton(QMessageBox.No)
				if msg_box.exec() == QMessageBox.Yes:
					list_wid = QTableWidget(self)
					list_wid.setAttribute(Qt.WA_DeleteOnClose)
					list_wid.setRowCount(len(s_list))
					list_wid.setColumnCount(2)
					list_wid.setAlternatingRowColors(True)
					list_wid.setEditTriggers(list_wid.NoEditTriggers)
					list_wid.setHorizontalHeaderLabels(['Reason', 'Path'])
					list_wid.setSelectionBehavior(list_wid.SelectRows)
					list_wid.setSelectionMode(list_wid.SingleSelection)
					list_wid.setSortingEnabled(True)
					list_wid.verticalHeader().hide()
					list_wid.setAutoScroll(False)
					for x, g in enumerate(s_list):
						list_wid.setItem(x, 0, QTableWidgetItem(g[1]))
						list_wid.setItem(x, 1, QTableWidgetItem(g[0]))
					list_wid.resizeColumnsToContents()
					list_wid.setWindowTitle('{} skipped paths'.format(len(s_list)))
					list_wid.setWindowFlags(Qt.Window)
					list_wid.resize(900,400)

					list_wid.doubleClicked.connect(lambda i: utils.open_path(
						list_wid.item(i.row(), 1).text(), list_wid.item(i.row(), 1).text()))

					list_wid.show()

			def a_progress(prog):
				loading.progress.setValue(prog)
				loading.setText("Preparing galleries...")

			self.g_populate_inst.moveToThread(data_thread)
			self.g_populate_inst.DATA_COUNT.connect(loading.progress.setMaximum)
			self.g_populate_inst.PROGRESS.connect(a_progress)
			self.g_populate_inst.FINISHED.connect(finished)
			self.g_populate_inst.FINISHED.connect(self.g_populate_inst.deleteLater)
			self.g_populate_inst.SKIPPED.connect(skipped_gs)
			data_thread.finished.connect(data_thread.deleteLater)
			data_thread.started.connect(self.g_populate_inst.local)
			data_thread.start()
			#.g_populate_inst.local()
			log_i('Populating DB from directory/archive')

	def scan_for_new_galleries(self):
		available_folders =  app_constants.ENABLE_MONITOR and\
									app_constants.MONITOR_PATHS and all(app_constants.MONITOR_PATHS)
		if available_folders and not app_constants.SCANNING_FOR_GALLERIES:
			app_constants.SCANNING_FOR_GALLERIES = True
			self.notification_bar.add_text("Scanning for new galleries...")
			log_i('Scanning for new galleries...')
			try:
				class ScanDir(QObject):
					final_paths_and_galleries = pyqtSignal(list, list)
					finished = pyqtSignal()
					def __init__(self, parent=None):
						super().__init__(parent)
						self.scanned_data = []
					def scan_dirs(self):
						paths = []
						for p in app_constants.MONITOR_PATHS:
							if os.path.exists(p):
								dir_content = scandir.scandir(p)
								for d in dir_content:
									paths.append(d.path)
							else:
								log_e("Monitored path does not exists: {}".format(p.encode(errors='ignore')))

						fetch_inst = fetch.Fetch(self)
						fetch_inst.series_path = paths
						def set_scanned_d(d):
							self.scanned_data = d
						fetch_inst.FINISHED.connect(set_scanned_d)
						fetch_inst.local()
						#contents = []
						#for g in self.scanned_data:
						#	contents.append(g)

						#paths = sorted(paths)
						#new_galleries = []
						#for x in contents:
						#	y = utils.b_search(paths, os.path.normcase(x.path))
						#	if not y:
						#		new_galleries.append(x)

						galleries = []
						final_paths = []
						if self.scanned_data:
							for g in self.scanned_data:
								try:
									if g.is_archive:
										g.profile = utils.get_gallery_img(g.chapters[0].path, g.path)
									else:
										g.profile = utils.get_gallery_img(g.chapters[0].path)
									if not g.profile:
										raise Exception
								except:
									g.profile = app_constants.NO_IMAGE_PATH
							
								galleries.append(g)
								final_paths.append(g.path)
						self.final_paths_and_galleries.emit(final_paths, galleries)
						self.finished.emit()
						self.deleteLater()
					#if app_constants.LOOK_NEW_GALLERY_AUTOADD:
					#	QTimer.singleShot(10000, self.gallery_populate(final_paths))
					#	return

				def show_new_galleries(final_paths, galleries):
					if final_paths and galleries:
						app_constants.OVERRIDE_MOVE_IMPORTED_IN_FETCH = True
						if app_constants.LOOK_NEW_GALLERY_AUTOADD:
							self.gallery_populate(final_paths)
						else:

							class NewGalleryMenu(QMenu):

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

									ignore_act = self.addAction('Add to ignore list')
									ignore_act.triggered.connect(self.add_to_ignore)

								def add_to_ignore(self):
									gallery = self.gallery_widget.gallery
									app_constants.IGNORE_PATHS.append(gallery.path)
									settings.set(app_constants.IGNORE_PATHS, 'Application', 'ignore paths')
									if self.gallery_widget.parent_widget.gallery_layout.count() == 1:
										self.gallery_widget.parent_widget.close()
									else:
										self.gallery_widget.close()

							if len(galleries) == 1:
								self.notification_bar.add_text("{} new gallery was discovered in one of your monitored directories".format(len(galleries)))
							else:
								self.notification_bar.add_text("{} new galleries were discovered in one of your monitored directories".format(len(galleries)))
							text = "These new galleries were discovered! Do you want to add them?"\
								if len(galleries) > 1 else "This new gallery was discovered! Do you want to add it?"
							g_popup = io_misc.GalleryPopup((text, galleries), self, NewGalleryMenu())
							buttons = g_popup.add_buttons('Add', 'Close')

							def populate_n_close():
								g_popup.close()
								self.gallery_populate(final_paths)
							buttons[0].clicked.connect(populate_n_close)
							buttons[1].clicked.connect(g_popup.close)

				def finished(): app_constants.SCANNING_FOR_GALLERIES = False

				thread = QThread(self)
				self.scan_inst = ScanDir()
				self.scan_inst.moveToThread(thread)
				self.scan_inst.final_paths_and_galleries.connect(show_new_galleries)
				self.scan_inst.finished.connect(finished)
				thread.started.connect(self.scan_inst.scan_dirs)
				#self.scan_inst.scan_dirs()
				thread.finished.connect(thread.deleteLater)
				thread.start()
			except:
				self.notification_bar.add_text('An error occured while attempting to scan for new galleries. Check happypanda.log.')
				log.exception('An error occured while attempting to scan for new galleries.')
				app_constants.SCANNING_FOR_GALLERIES = False

	def dragEnterEvent(self, event):
		if event.mimeData().hasUrls():
			event.acceptProposedAction()
		else:
			self.notification_bar.add_text('File is not supported')

	def dropEvent(self, event):
		acceptable = []
		unaccept = []
		for u in event.mimeData().urls():
			path = u.toLocalFile()
			if os.path.isdir(path) or path.endswith(utils.ARCHIVE_FILES):
				acceptable.append(path)
			else:
				unaccept.append(path)
		log_i('Acceptable dropped items: {}'.format(len(acceptable)))
		log_i('Unacceptable dropped items: {}'.format(len(unaccept)))
		log_d('Dropped items: {}\n{}'.format(acceptable, unaccept).encode(errors='ignore'))
		if acceptable:
			self.notification_bar.add_text('Adding dropped items...')
			log_i('Adding dropped items')
			l = len(acceptable) == 1
			f_item = acceptable[0]
			if f_item.endswith(utils.ARCHIVE_FILES):
				f_item = utils.check_archive(f_item)
			else:
				f_item = utils.recursive_gallery_check(f_item)
			f_item_l = len(f_item) < 2
			subfolder_as_c = not app_constants.SUBFOLDER_AS_GALLERY
			if l and subfolder_as_c or l and f_item_l:
				g_d = gallerydialog.GalleryDialog(self, acceptable[0])
				g_d.SERIES.connect(self.manga_list_view.gallery_model.addRows)
				g_d.show()
			else:
				self.gallery_populate(acceptable, True)
		else:
			text = 'File not supported' if len(unaccept) < 2 else 'Files not supported'
			self.notification_bar.add_text(text)

		if unaccept:
			self.notification_bar.add_text('Some unsupported files did not get added')

	def resizeEvent(self, event):
		try:
			self.notification_bar.resize(event.size().width())
		except AttributeError:
			pass
		self.move_listener.emit()
		return super().resizeEvent(event)

	def moveEvent(self, event):
		self.move_listener.emit()
		return super().moveEvent(event)

	def showEvent(self, event):
		return super().showEvent(event)

	def cleanup_exit(self):
		self.system_tray.hide()
		# watchers
		try:
			self.watchers.stop_all()
		except AttributeError:
			pass

		# settings
		settings.set(self.manga_list_view.current_sort, 'General', 'current sort')
		settings.set(app_constants.IGNORE_PATHS, 'Application', 'ignore paths')
		settings.win_save(self, 'AppWindow')

		# temp dir
		try:
			for root, dirs, files in scandir.walk('temp', topdown=False):
				for name in files:
					os.remove(os.path.join(root, name))
				for name in dirs:
					os.rmdir(os.path.join(root, name))
			log_d('Flush temp on exit: OK')
		except:
			log.exception('Flush temp on exit: FAIL')

		if self.tags_treeview:
			self.tags_treeview.close()
		self.download_window.close()

		# check if there is db activity
		if not gallerydb.method_queue.empty():
			class DBActivityChecker(QObject):
				FINISHED = pyqtSignal()
				def __init__(self, **kwargs):
					super().__init__(**kwargs)

				def check(self):
					gallerydb.method_queue.join()
					self.FINISHED.emit()
					self.deleteLater()

			db_activity = DBActivityChecker()
			db_spinner = misc.Spinner(self)
			self.db_activity_checker.connect(db_activity.check)
			db_activity.moveToThread(app_constants.GENERAL_THREAD)
			db_activity.FINISHED.connect(db_spinner.close)
			db_spinner.set_size(50)
			db_spinner.set_text('Activity')
			db_spinner.move(QPoint(self.pos().x()+self.width()-70, self.pos().y()+self.height()-70))
			self.move_listener.connect(lambda: db_spinner.update_move(QPoint(self.pos().x()+self.width()-70,
																	self.pos().y()+self.height()-70)))
			db_spinner.show()
			self.db_activity_checker.emit()
			msg_box = QMessageBox(self)
			msg_box.setText('Database activity detected!')
			msg_box.setInformativeText("Closing now might result in data loss." +
								 " Do you still want to close?\n(Wait for the activity spinner to hide before closing)")
			msg_box.setIcon(QMessageBox.Critical)
			msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
			msg_box.setDefaultButton(QMessageBox.No)
			if msg_box.exec() == QMessageBox.Yes:
				return 1
			else:
				return 2
		else:
			return 0

	def closeEvent(self, event):
		r_code = self.cleanup_exit()
		if r_code == 1:
			log_d('Force Exit App: OK')
			super().closeEvent(event)
		elif r_code == 2:
			log_d('Ignore Exit App')
			event.ignore()
		else:
			log_d('Normal Exit App: OK')
			super().closeEvent(event)
Esempio n. 50
0
class SettingsDialog(QWidget):
	"A settings dialog"
	scroll_speed_changed = pyqtSignal()
	init_gallery_rebuild = pyqtSignal(bool)
	init_gallery_eximport = pyqtSignal(object)
	def __init__(self, parent=None):
		super().__init__(parent, flags=Qt.Window)

		self.init_gallery_rebuild.connect(self.accept)

		self.parent_widget = parent
		self.setAttribute(Qt.WA_DeleteOnClose)
		self.resize(700, 500)
		self.restore_values()
		self.initUI()
		self.setWindowTitle('Settings')
		self.show()

	def initUI(self):
		main_layout = QVBoxLayout(self)
		sub_layout = QHBoxLayout()
		# Left Panel
		left_panel = QListWidget()
		left_panel.setViewMode(left_panel.ListMode)
		#left_panel.setIconSize(QSize(40,40))
		left_panel.setTextElideMode(Qt.ElideRight)
		left_panel.setMaximumWidth(200)
		left_panel.itemClicked.connect(self.change)
		#web.setText('Web')
		self.application = QListWidgetItem()
		self.application.setText('Application')
		self.web = QListWidgetItem()
		self.web.setText('Web')
		self.visual = QListWidgetItem()
		self.visual.setText('Visual')
		self.advanced = QListWidgetItem()
		self.advanced.setText('Advanced')
		self.about = QListWidgetItem()
		self.about.setText('About')

		#main.setIcon(QIcon(os.path.join(app_constants.static_dir, 'plus2.png')))
		left_panel.addItem(self.application)
		left_panel.addItem(self.web)
		left_panel.addItem(self.visual)
		left_panel.addItem(self.advanced)
		left_panel.addItem(self.about)
		left_panel.setMaximumWidth(100)

		# right panel
		self.right_panel = QStackedLayout()
		self.init_right_panel()

		# bottom
		bottom_layout = QHBoxLayout()
		ok_btn = QPushButton('Ok')
		ok_btn.clicked.connect(self.accept)
		cancel_btn = QPushButton('Cancel')
		cancel_btn.clicked.connect(self.close)
		info_lbl = QLabel()
		info_lbl.setText('<a href="https://github.com/Pewpews/happypanda">'+
				   'Visit GitHub Repo</a> | Options marked with * requires application restart.')
		info_lbl.setTextFormat(Qt.RichText)
		info_lbl.setTextInteractionFlags(Qt.TextBrowserInteraction)
		info_lbl.setOpenExternalLinks(True)
		self.spacer = QWidget()
		self.spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
		bottom_layout.addWidget(info_lbl, 0, Qt.AlignLeft)
		bottom_layout.addWidget(self.spacer)
		bottom_layout.addWidget(ok_btn, 0, Qt.AlignRight)
		bottom_layout.addWidget(cancel_btn, 0, Qt.AlignRight)

		sub_layout.addWidget(left_panel)
		sub_layout.addLayout(self.right_panel)
		main_layout.addLayout(sub_layout)
		main_layout.addLayout(bottom_layout)

		self.restore_options()


	def change(self, item):
		def curr_index(index):
			if index != self.right_panel.currentIndex():
				self.right_panel.setCurrentIndex(index)
		if item == self.application:
			curr_index(self.application_index)
		elif item == self.web:
			curr_index(self.web_index)
		elif item == self.visual:
			curr_index(self.visual_index)
		elif item == self.advanced:
			curr_index(self.advanced_index)
		elif item == self.about:
			curr_index(self.about_index)

	def restore_values(self):
		#Web
		self.exprops = settings.ExProperties()

		# Visual
		self.high_quality_thumbs = app_constants.HIGH_QUALITY_THUMBS
		self.popup_width = app_constants.POPUP_WIDTH
		self.popup_height = app_constants.POPUP_HEIGHT
		self.style_sheet = app_constants.user_stylesheet_path

		# Advanced
		self.scroll_speed = app_constants.SCROLL_SPEED
		self.cache_size = app_constants.THUMBNAIL_CACHE_SIZE
		self.prefetch_item_amnt = app_constants.PREFETCH_ITEM_AMOUNT

	def restore_options(self):

		# App / General
		self.subfolder_as_chapters.setChecked(app_constants.SUBFOLDER_AS_GALLERY)
		self.extract_gallery_before_opening.setChecked(app_constants.EXTRACT_CHAPTER_BEFORE_OPENING)
		self.open_galleries_sequentially.setChecked(app_constants.OPEN_GALLERIES_SEQUENTIALLY)
		self.scroll_to_new_gallery.setChecked(app_constants.SCROLL_TO_NEW_GALLERIES)
		self.move_imported_gs.setChecked(app_constants.MOVE_IMPORTED_GALLERIES)
		self.move_imported_def_path.setText(app_constants.IMPORTED_GALLERY_DEF_PATH)
		self.open_random_g_chapters.setChecked(app_constants.OPEN_RANDOM_GALLERY_CHAPTERS)
		self.rename_g_source_group.setChecked(app_constants.RENAME_GALLERY_SOURCE)
		self.path_to_unrar.setText(app_constants.unrar_tool_path)
		# App / General / External Viewer
		self.external_viewer_path.setText(app_constants.EXTERNAL_VIEWER_PATH)

		# App / Monitor / Misc
		self.enable_monitor.setChecked(app_constants.ENABLE_MONITOR)
		self.look_new_gallery_startup.setChecked(app_constants.LOOK_NEW_GALLERY_STARTUP)
		self.auto_add_new_galleries.setChecked(app_constants.LOOK_NEW_GALLERY_AUTOADD)
		# App / Monitor / Folders
		for path in app_constants.MONITOR_PATHS:
			self.add_folder_monitor(path)

		# App / Monitor / Ignore list
		for path in app_constants.IGNORE_PATHS:
			self.add_ignore_path(path)

		# Web / General
		if 'g.e-hentai' in app_constants.DEFAULT_EHEN_URL:
			self.default_ehen_url.setChecked(True)
		else:
			self.exhentai_ehen_url.setChecked(True)
		
		self.replace_metadata.setChecked(app_constants.REPLACE_METADATA)
		self.always_first_hit.setChecked(app_constants.ALWAYS_CHOOSE_FIRST_HIT)
		self.web_time_offset.setValue(app_constants.GLOBAL_EHEN_TIME)
		self.continue_a_metadata_fetcher.setChecked(app_constants.CONTINUE_AUTO_METADATA_FETCHER)
		self.use_jpn_title.setChecked(app_constants.USE_JPN_TITLE)
		self.use_gallery_link.setChecked(app_constants.USE_GALLERY_LINK)

		# Web / Download
		if app_constants.HEN_DOWNLOAD_TYPE == 0:
			self.archive_download.setChecked(True)
		else:
			self.torrent_download.setChecked(True)

		self.download_directory.setText(app_constants.DOWNLOAD_DIRECTORY)
		self.torrent_client.setText(app_constants.TORRENT_CLIENT)

		# Web / Exhentai
		self.ipbid_edit.setText(self.exprops.ipb_id)
		self.ipbpass_edit.setText(self.exprops.ipb_pass)

		# Visual / Grid View / Tooltip
		self.grid_tooltip_group.setChecked(app_constants.GRID_TOOLTIP)
		self.visual_grid_tooltip_title.setChecked(app_constants.TOOLTIP_TITLE)
		self.visual_grid_tooltip_author.setChecked(app_constants.TOOLTIP_AUTHOR)
		self.visual_grid_tooltip_chapters.setChecked(app_constants.TOOLTIP_CHAPTERS)
		self.visual_grid_tooltip_status.setChecked(app_constants.TOOLTIP_STATUS)
		self.visual_grid_tooltip_type.setChecked(app_constants.TOOLTIP_TYPE)
		self.visual_grid_tooltip_lang.setChecked(app_constants.TOOLTIP_LANG)
		self.visual_grid_tooltip_descr.setChecked(app_constants.TOOLTIP_DESCR)
		self.visual_grid_tooltip_tags.setChecked(app_constants.TOOLTIP_TAGS)
		self.visual_grid_tooltip_last_read.setChecked(app_constants.TOOLTIP_LAST_READ)
		self.visual_grid_tooltip_times_read.setChecked(app_constants.TOOLTIP_TIMES_READ)
		self.visual_grid_tooltip_pub_date.setChecked(app_constants.TOOLTIP_PUB_DATE)
		self.visual_grid_tooltip_date_added.setChecked(app_constants.TOOLTIP_DATE_ADDED)
		# Visual / Grid View / Gallery
		self.external_viewer_ico.setChecked(app_constants.USE_EXTERNAL_PROG_ICO)
		self.gallery_type_ico.setChecked(app_constants.DISPLAY_GALLERY_TYPE)
		if app_constants.GALLERY_FONT_ELIDE:
			self.gallery_text_elide.setChecked(True)
		else:
			self.gallery_text_fit.setChecked(True)
		self.font_lbl.setText(app_constants.GALLERY_FONT[0])
		self.font_size_lbl.setValue(app_constants.GALLERY_FONT[1])

		def re_enforce(s):
			if s:
				self.search_on_enter.setChecked(True)
		self.search_allow_regex.clicked.connect(re_enforce)

		if app_constants.SEARCH_ON_ENTER:
			self.search_on_enter.setChecked(True)
		else:
			self.search_every_keystroke.setChecked(True)
		# Visual / Grid View / Colors
		self.grid_label_color.setText(app_constants.GRID_VIEW_LABEL_COLOR)
		self.grid_title_color.setText(app_constants.GRID_VIEW_TITLE_COLOR)
		self.grid_artist_color.setText(app_constants.GRID_VIEW_ARTIST_COLOR)


		# Advanced / Gallery / Gallery Text Fixer
		self.g_data_regex_fix_edit.setText(app_constants.GALLERY_DATA_FIX_REGEX)
		self.g_data_replace_fix_edit.setText(app_constants.GALLERY_DATA_FIX_REPLACE)
		self.g_data_fixer_title.setChecked(app_constants.GALLERY_DATA_FIX_TITLE)
		self.g_data_fixer_artist.setChecked(app_constants.GALLERY_DATA_FIX_ARTIST)

		# About / DB Overview
		self.tags_treeview_on_start.setChecked(app_constants.TAGS_TREEVIEW_ON_START)

	def accept(self):
		set = settings.set

		# App / General / Gallery
		app_constants.SUBFOLDER_AS_GALLERY = self.subfolder_as_chapters.isChecked()
		set(app_constants.SUBFOLDER_AS_GALLERY, 'Application', 'subfolder as gallery')
		app_constants.EXTRACT_CHAPTER_BEFORE_OPENING = self.extract_gallery_before_opening.isChecked()
		set(app_constants.EXTRACT_CHAPTER_BEFORE_OPENING, 'Application', 'extract chapter before opening')
		app_constants.OPEN_GALLERIES_SEQUENTIALLY = self.open_galleries_sequentially.isChecked()
		set(app_constants.OPEN_GALLERIES_SEQUENTIALLY, 'Application', 'open galleries sequentially')
		app_constants.SCROLL_TO_NEW_GALLERIES = self.scroll_to_new_gallery.isChecked()
		set(app_constants.SCROLL_TO_NEW_GALLERIES, 'Application', 'scroll to new galleries')
		app_constants.MOVE_IMPORTED_GALLERIES = self.move_imported_gs.isChecked()
		set(app_constants.MOVE_IMPORTED_GALLERIES, 'Application', 'move imported galleries')
		if not self.move_imported_def_path.text() or os.path.exists(self.move_imported_def_path.text()):
			app_constants.IMPORTED_GALLERY_DEF_PATH = self.move_imported_def_path.text()
			set(app_constants.IMPORTED_GALLERY_DEF_PATH, 'Application', 'imported gallery def path')
		app_constants.OPEN_RANDOM_GALLERY_CHAPTERS = self.open_random_g_chapters.isChecked()
		set(app_constants.OPEN_RANDOM_GALLERY_CHAPTERS, 'Application', 'open random gallery chapters')
		app_constants.RENAME_GALLERY_SOURCE = self.rename_g_source_group.isChecked()
		set(app_constants.RENAME_GALLERY_SOURCE, 'Application', 'rename gallery source')
		app_constants.unrar_tool_path = self.path_to_unrar.text()
		set(app_constants.unrar_tool_path, 'Application', 'unrar tool path')
		# App / General / Search
		app_constants.ALLOW_SEARCH_REGEX = self.search_allow_regex.isChecked()
		set(app_constants.ALLOW_SEARCH_REGEX, 'Application', 'allow search regex')
		app_constants.SEARCH_AUTOCOMPLETE = self.search_autocomplete.isChecked()
		set(app_constants.SEARCH_AUTOCOMPLETE, 'Application', 'search autocomplete')
		if self.search_on_enter.isChecked():
			app_constants.SEARCH_ON_ENTER = True
		else:
			app_constants.SEARCH_ON_ENTER = False
		set(app_constants.SEARCH_ON_ENTER, 'Application', 'search on enter')
		# App / General / External Viewer
		if not self.external_viewer_path.text():
			app_constants.USE_EXTERNAL_VIEWER = False
			set(False, 'Application', 'use external viewer')
		else:
			app_constants.USE_EXTERNAL_VIEWER = True
			set(True, 'Application', 'use external viewer')
			app_constants._REFRESH_EXTERNAL_VIEWER = True
		app_constants.EXTERNAL_VIEWER_PATH = self.external_viewer_path.text()
		set(app_constants.EXTERNAL_VIEWER_PATH,'Application', 'external viewer path')
		# App / Monitor / misc
		app_constants.ENABLE_MONITOR = self.enable_monitor.isChecked()
		set(app_constants.ENABLE_MONITOR, 'Application', 'enable monitor')
		app_constants.LOOK_NEW_GALLERY_STARTUP = self.look_new_gallery_startup.isChecked()
		set(app_constants.LOOK_NEW_GALLERY_STARTUP, 'Application', 'look new gallery startup')
		app_constants.LOOK_NEW_GALLERY_AUTOADD = self.auto_add_new_galleries.isChecked()
		set(app_constants.LOOK_NEW_GALLERY_AUTOADD, 'Application', 'look new gallery autoadd')
		# App / Monitor / folders
		paths = []
		folder_p_widgets = self.take_all_layout_widgets(self.folders_layout)
		for x, l_edit in enumerate(folder_p_widgets):
			p = l_edit.text()
			if p:
				paths.append(p)

		set(paths, 'Application', 'monitor paths')
		app_constants.MONITOR_PATHS = paths
		# App / Monitor / ignore list
		paths = []
		ignore_p_widgets = self.take_all_layout_widgets(self.ignore_path_l)
		for x, l_edit in enumerate(ignore_p_widgets):
			p = l_edit.text()
			if p:
				paths.append(p)
		set(paths, 'Application', 'ignore paths')
		app_constants.IGNORE_PATHS = paths

		# Web / Downloader

		if self.archive_download.isChecked():
			app_constants.HEN_DOWNLOAD_TYPE = 0
		else:
			app_constants.HEN_DOWNLOAD_TYPE = 1
		set(app_constants.HEN_DOWNLOAD_TYPE, 'Web', 'hen download type')

		app_constants.DOWNLOAD_DIRECTORY = self.download_directory.text()
		set(app_constants.DOWNLOAD_DIRECTORY, 'Web', 'download directory')

		app_constants.TORRENT_CLIENT = self.torrent_client.text()
		set(app_constants.TORRENT_CLIENT, 'Web', 'torrent client')

		# Web / Metdata
		if self.default_ehen_url.isChecked():
			app_constants.DEFAULT_EHEN_URL = 'http://g.e-hentai.org/'
		else:
			app_constants.DEFAULT_EHEN_URL = 'http://exhentai.org/'
		set(app_constants.DEFAULT_EHEN_URL, 'Web', 'default ehen url')

		app_constants.REPLACE_METADATA = self.replace_metadata.isChecked()
		set(app_constants.REPLACE_METADATA, 'Web', 'replace metadata')

		app_constants.ALWAYS_CHOOSE_FIRST_HIT = self.always_first_hit.isChecked()
		set(app_constants.ALWAYS_CHOOSE_FIRST_HIT, 'Web', 'always choose first hit')

		app_constants.GLOBAL_EHEN_TIME = self.web_time_offset.value()
		set(app_constants.GLOBAL_EHEN_TIME, 'Web', 'global ehen time offset')

		app_constants.CONTINUE_AUTO_METADATA_FETCHER = self.continue_a_metadata_fetcher.isChecked()
		set(app_constants.CONTINUE_AUTO_METADATA_FETCHER, 'Web', 'continue auto metadata fetcher')

		app_constants.USE_JPN_TITLE = self.use_jpn_title.isChecked()
		set(app_constants.USE_JPN_TITLE, 'Web', 'use jpn title')

		app_constants.USE_GALLERY_LINK = self.use_gallery_link.isChecked()
		set(app_constants.USE_GALLERY_LINK, 'Web', 'use gallery link')

		# Web / ExHentai
		self.exprops.ipb_id = self.ipbid_edit.text()
		self.exprops.ipb_pass = self.ipbpass_edit.text()

		# Visual / Grid View / Tooltip
		app_constants.GRID_TOOLTIP = self.grid_tooltip_group.isChecked()
		set(app_constants.GRID_TOOLTIP, 'Visual', 'grid tooltip')
		app_constants.TOOLTIP_TITLE = self.visual_grid_tooltip_title.isChecked()
		set(app_constants.TOOLTIP_TITLE, 'Visual', 'tooltip title')
		app_constants.TOOLTIP_AUTHOR = self.visual_grid_tooltip_author.isChecked()
		set(app_constants.TOOLTIP_AUTHOR, 'Visual', 'tooltip author')
		app_constants.TOOLTIP_CHAPTERS = self.visual_grid_tooltip_chapters.isChecked()
		set(app_constants.TOOLTIP_CHAPTERS, 'Visual', 'tooltip chapters')
		app_constants.TOOLTIP_STATUS = self.visual_grid_tooltip_status.isChecked()
		set(app_constants.TOOLTIP_STATUS, 'Visual', 'tooltip status')
		app_constants.TOOLTIP_TYPE = self.visual_grid_tooltip_type.isChecked()
		set(app_constants.TOOLTIP_TYPE, 'Visual', 'tooltip type')
		app_constants.TOOLTIP_LANG = self.visual_grid_tooltip_lang.isChecked()
		set(app_constants.TOOLTIP_LANG, 'Visual', 'tooltip lang')
		app_constants.TOOLTIP_DESCR = self.visual_grid_tooltip_descr.isChecked()
		set(app_constants.TOOLTIP_DESCR, 'Visual', 'tooltip descr')
		app_constants.TOOLTIP_TAGS = self.visual_grid_tooltip_tags.isChecked()
		set(app_constants.TOOLTIP_TAGS, 'Visual', 'tooltip tags')
		app_constants.TOOLTIP_LAST_READ = self.visual_grid_tooltip_last_read.isChecked()
		set(app_constants.TOOLTIP_LAST_READ, 'Visual', 'tooltip last read')
		app_constants.TOOLTIP_TIMES_READ = self.visual_grid_tooltip_times_read.isChecked()
		set(app_constants.TOOLTIP_TIMES_READ, 'Visual', 'tooltip times read')
		app_constants.TOOLTIP_PUB_DATE = self.visual_grid_tooltip_pub_date.isChecked()
		set(app_constants.TOOLTIP_PUB_DATE, 'Visual', 'tooltip pub date')
		app_constants.TOOLTIP_DATE_ADDED = self.visual_grid_tooltip_date_added.isChecked()
		set(app_constants.TOOLTIP_DATE_ADDED, 'Visual', 'tooltip date added')
		# Visual / Grid View / Gallery
		app_constants.USE_EXTERNAL_PROG_ICO = self.external_viewer_ico.isChecked()
		set(app_constants.USE_EXTERNAL_PROG_ICO, 'Visual', 'use external prog ico')
		app_constants.DISPLAY_GALLERY_TYPE = self.gallery_type_ico.isChecked()
		set(app_constants.DISPLAY_GALLERY_TYPE, 'Visual', 'display gallery type')
		if self.gallery_text_elide.isChecked():
			app_constants.GALLERY_FONT_ELIDE = True
		else:
			app_constants.GALLERY_FONT_ELIDE = False
		set(app_constants.GALLERY_FONT_ELIDE, 'Visual', 'gallery font elide')
		app_constants.GALLERY_FONT = (self.font_lbl.text(), self.font_size_lbl.value())
		set(app_constants.GALLERY_FONT[0], 'Visual', 'gallery font family')
		set(app_constants.GALLERY_FONT[1], 'Visual', 'gallery font size')
		# Visual / Grid View / Colors
		if self.color_checker(self.grid_title_color.text()):
			app_constants.GRID_VIEW_TITLE_COLOR = self.grid_title_color.text()
			set(app_constants.GRID_VIEW_TITLE_COLOR, 'Visual', 'grid view title color')
		if self.color_checker(self.grid_artist_color.text()):
			app_constants.GRID_VIEW_ARTIST_COLOR = self.grid_artist_color.text()
			set(app_constants.GRID_VIEW_ARTIST_COLOR, 'Visual', 'grid view artist color')
		if self.color_checker(self.grid_label_color.text()):
			app_constants.GRID_VIEW_LABEL_COLOR = self.grid_label_color.text()
			set(app_constants.GRID_VIEW_LABEL_COLOR, 'Visual', 'grid view label color')

		# Advanced / Misc
		# Advanced / Misc / Grid View
		app_constants.SCROLL_SPEED = self.scroll_speed
		set(self.scroll_speed, 'Advanced', 'scroll speed')
		self.scroll_speed_changed.emit()
		app_constants.THUMBNAIL_CACHE_SIZE = self.cache_size
		set(self.cache_size[1], 'Advanced', 'cache size')
		QPixmapCache.setCacheLimit(self.cache_size[0]*
							 self.cache_size[1])


		# Advanced / General / Gallery Text Fixer
		app_constants.GALLERY_DATA_FIX_REGEX = self.g_data_regex_fix_edit.text()
		set(app_constants.GALLERY_DATA_FIX_REGEX, 'Advanced', 'gallery data fix regex')
		app_constants.GALLERY_DATA_FIX_TITLE = self.g_data_fixer_title.isChecked()
		set(app_constants.GALLERY_DATA_FIX_TITLE, 'Advanced', 'gallery data fix title')
		app_constants.GALLERY_DATA_FIX_ARTIST = self.g_data_fixer_artist.isChecked()
		set(app_constants.GALLERY_DATA_FIX_ARTIST, 'Advanced', 'gallery data fix artist')
		app_constants.GALLERY_DATA_FIX_REPLACE = self.g_data_replace_fix_edit.text()
		set(app_constants.GALLERY_DATA_FIX_REPLACE, 'Advanced', 'gallery data fix replace')

		# About / DB Overview
		app_constants.TAGS_TREEVIEW_ON_START = self.tags_treeview_on_start.isChecked()
		set(app_constants.TAGS_TREEVIEW_ON_START, 'Application', 'tags treeview on start')

		settings.save()
		self.close()

	def init_right_panel(self):

		#def title_def(title):
		#	title_lbl = QLabel(title)
		#	f = QFont()
		#	f.setPixelSize(16)
		#	title_lbl.setFont(f)
		#	return title_lbl

		def groupbox(name, layout, parent, add_in_layout=None):
			"""
			Makes a groupbox and a layout for you
			Returns groupbox and layout
			"""
			g = QGroupBox(name, parent)
			l = layout(g)
			if add_in_layout:
				if isinstance(add_in_layout, QFormLayout):
					add_in_layout.addRow(g)
				else:
					add_in_layout.addWidget(g)
			return g, l

		def option_lbl_checkbox(text, optiontext, parent=None):
			l = QLabel(text)
			c = QCheckBox(text, parent)
			return l, c

		def new_tab(name, parent, scroll=False):
			"""
			Creates a new tab.
			Returns new tab page widget and it's layout
			"""
			new_t = QWidget(parent)
			new_l = QFormLayout(new_t)
			if scroll:
				scr = QScrollArea(parent)
				scr.setBackgroundRole(QPalette.Base)
				scr.setWidget(new_t)
				scr.setWidgetResizable(True)
				parent.addTab(scr, name)
				return new_t, new_l
			else:
				parent.addTab(new_t, name)
			return new_t, new_l


		# App
		application = QTabWidget(self)
		self.application_index = self.right_panel.addWidget(application)
		application_general, app_general_m_l = new_tab('General', application, True)

		# App / General / gallery
		app_gallery_page, app_gallery_l = new_tab('Gallery', application, True)
		self.subfolder_as_chapters = QCheckBox("Subdirectiories should be treated as standalone galleries instead of chapters (applies in archives too)")
		self.subfolder_as_chapters.setToolTip("This option will enable creating standalone galleries for each subdirectiories found recursively when importing."+
										"\nDefault action is treating each subfolder found as chapters of a gallery.")
		extract_gallery_info = QLabel("Note: This option has no effect when turned off if path to viewer is not specified.")
		self.extract_gallery_before_opening = QCheckBox("Extract archive before opening (only turn off if your viewer supports it)")
		self.open_galleries_sequentially = QCheckBox("Open chapters sequentially (Note: has no effect if path to viewer is not specified)")
		subf_info = QLabel("Behaviour of 'Scan for new galleries on startup' option will be affected.")
		subf_info.setWordWrap(True)
		app_gallery_l.addRow('Note:', subf_info)
		app_gallery_l.addRow(self.subfolder_as_chapters)
		app_gallery_l.addRow(extract_gallery_info)
		app_gallery_l.addRow(self.extract_gallery_before_opening)
		app_gallery_l.addRow(self.open_galleries_sequentially)
		self.scroll_to_new_gallery = QCheckBox("Scroll to newly added gallery")
		self.scroll_to_new_gallery.setDisabled(True)
		app_gallery_l.addRow(self.scroll_to_new_gallery)
		self.move_imported_gs, move_imported_gs_l = groupbox('Move imported galleries',
													   QFormLayout, app_gallery_page)
		self.move_imported_gs.setCheckable(True)
		self.move_imported_gs.setToolTip("Move imported galleries to specified folder.")
		self.move_imported_def_path = PathLineEdit()
		move_imported_gs_l.addRow('Directory:', self.move_imported_def_path)
		app_gallery_l.addRow(self.move_imported_gs)
		self.rename_g_source_group, rename_g_source_l = groupbox('Rename gallery source',
													  QFormLayout, app_gallery_page)
		self.rename_g_source_group.setCheckable(True)
		self.rename_g_source_group.setDisabled(True)
		app_gallery_l.addRow(self.rename_g_source_group)
		rename_g_source_l.addRow(QLabel("Check what to include when renaming gallery source. (Same order)"))
		rename_g_source_flow_l = FlowLayout()
		rename_g_source_l.addRow(rename_g_source_flow_l)
		self.rename_artist = QCheckBox("Artist")
		self.rename_title = QCheckBox("Title")
		self.rename_lang = QCheckBox("Language")
		self.rename_title.setChecked(True)
		self.rename_title.setDisabled(True)
		rename_g_source_flow_l.addWidget(self.rename_artist)
		rename_g_source_flow_l.addWidget(self.rename_title)
		rename_g_source_flow_l.addWidget(self.rename_lang)
		random_gallery_opener, random_g_opener_l = groupbox('Random Gallery Opener', QFormLayout, app_gallery_page)
		app_gallery_l.addRow(random_gallery_opener)
		self.open_random_g_chapters = QCheckBox("Open random gallery chapters")
		random_g_opener_l.addRow(self.open_random_g_chapters)

		# App / General / Search
		app_search, app_search_layout = groupbox('Search', QFormLayout, application_general)
		app_general_m_l.addRow(app_search)
		search_allow_regex_l = QHBoxLayout()
		self.search_allow_regex = QCheckBox()
		self.search_allow_regex.setChecked(app_constants.ALLOW_SEARCH_REGEX)
		self.search_allow_regex.adjustSize()
		self.search_allow_regex.setToolTip('A regex cheatsheet is located at About->Regex Cheatsheet')
		search_allow_regex_l.addWidget(self.search_allow_regex)
		search_allow_regex_l.addWidget(QLabel('A regex cheatsheet is located at About->Regex Cheatsheet'))
		search_allow_regex_l.addWidget(Spacer('h'))
		app_search_layout.addRow('Regex:', search_allow_regex_l)
		# App / General / Search / autocomplete
		self.search_autocomplete = QCheckBox('*')
		self.search_autocomplete.setChecked(app_constants.SEARCH_AUTOCOMPLETE)
		self.search_autocomplete.setToolTip('Turn autocomplete on/off')
		app_search_layout.addRow('Autocomplete', self.search_autocomplete)
		# App / General / Search / search behaviour
		self.search_every_keystroke = QRadioButton('Search on every keystroke *', app_search)
		app_search_layout.addRow(self.search_every_keystroke)
		self.search_on_enter = QRadioButton('Search on return-key *', app_search)
		app_search_layout.addRow(self.search_on_enter)

		# App / General / External Viewer
		app_external_viewer, app_external_viewer_l = groupbox('External Viewer', QFormLayout, application_general, app_general_m_l)
		app_external_viewer_l.addRow(QLabel("Most image viewers should work. Incase it doesn't," +
									   " hit me up on email/github/gitter-chat to add support."))
		self.external_viewer_path = PathLineEdit(app_external_viewer, False, '')
		self.external_viewer_path.setPlaceholderText('Right/Left-click to open folder explorer.'+
							  ' Leave empty to use default viewer')
		self.external_viewer_path.setToolTip('Right/Left-click to open folder explorer.'+
							  ' Leave empty to use default viewer')
		self.external_viewer_path.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
		app_external_viewer_l.addRow('Path:', self.external_viewer_path)

		# App / General / Rar Support
		app_rar_group, app_rar_layout = groupbox('RAR Support *', QFormLayout, self)
		app_general_m_l.addRow(app_rar_group)
		rar_info = QLabel('Specify the path to the unrar tool to enable rar support.\n'+
					'Windows: "unrar.exe" should be in the "bin" directory if you installed from the'+
					' self-extracting archive provided on github.\nOSX: You can install this via HomeBrew.'+
					' Path should be something like: "/usr/local/bin/unrar".\nLinux: Should already be'+
					' installed. You can just type "unrar". If it\'s not installed, use your package manager: pacman -S unrar')
		rar_info.setWordWrap(True)
		app_rar_layout.addRow(rar_info)
		self.path_to_unrar = PathLineEdit(self, False, filters='')
		app_rar_layout.addRow('UnRAR tool path:', self.path_to_unrar)

		# App / Monitor
		app_monitor_page = QScrollArea()
		app_monitor_page.setBackgroundRole(QPalette.Base)
		app_monitor_dummy = QWidget()
		app_monitor_page.setWidgetResizable(True)
		app_monitor_page.setWidget(app_monitor_dummy)
		application.addTab(app_monitor_page, 'Monitoring')
		app_monitor_m_l = QVBoxLayout(app_monitor_dummy)
		# App / Monitor / misc
		app_monitor_misc_group = QGroupBox('General *', self)
		app_monitor_m_l.addWidget(app_monitor_misc_group)
		app_monitor_misc_m_l = QFormLayout(app_monitor_misc_group)
		monitor_info = QLabel('Directory monitoring will monitor the specified directories for any'+
						' filesystem events. For example if you delete a gallery source in one of your'+
						' monitored directories the application will inform you and ask if'+
						' you want to delete the gallery from the application as well.')
		monitor_info.setWordWrap(True)
		app_monitor_misc_m_l.addRow(monitor_info)
		self.enable_monitor = QCheckBox('Enable directory monitoring')
		app_monitor_misc_m_l.addRow(self.enable_monitor)
		self.look_new_gallery_startup = QGroupBox('Scan for new galleries on startup', self)
		app_monitor_misc_m_l.addRow(self.look_new_gallery_startup)
		self.look_new_gallery_startup.setCheckable(True)
		look_new_gallery_startup_m_l = QVBoxLayout(self.look_new_gallery_startup)
		self.auto_add_new_galleries = QCheckBox('Automatically add found galleries')
		look_new_gallery_startup_m_l.addWidget(self.auto_add_new_galleries)

		# App / Monitor / folders
		app_monitor_group = QGroupBox('Directories *', self)
		app_monitor_m_l.addWidget(app_monitor_group, 1)
		app_monitor_folders_m_l = QVBoxLayout(app_monitor_group)
		app_monitor_folders_add = QPushButton('+')
		app_monitor_folders_add.clicked.connect(self.add_folder_monitor)
		app_monitor_folders_add.setMaximumWidth(20)
		app_monitor_folders_add.setMaximumHeight(20)
		app_monitor_folders_m_l.addWidget(app_monitor_folders_add, 0, Qt.AlignRight)
		self.folders_layout = QFormLayout()
		app_monitor_folders_m_l.addLayout(self.folders_layout)

		# App / Ignore
		app_ignore, app_ignore_m_l = new_tab('Ignore', application, True)
		app_ignore_group, app_ignore_list_l = groupbox('List', QVBoxLayout, app_monitor_dummy)
		app_ignore_m_l.addRow(app_ignore_group)
		add_buttons_l = QHBoxLayout()
		app_ignore_add_a = QPushButton('Add archive')
		app_ignore_add_a.clicked.connect(lambda: self.add_ignore_path(dir=False))
		app_ignore_add_f = QPushButton('Add directory')
		app_ignore_add_f.clicked.connect(self.add_ignore_path)
		add_buttons_l.addWidget(app_ignore_add_a, 0, Qt.AlignRight)
		add_buttons_l.addWidget(app_ignore_add_f, 1, Qt.AlignRight)
		app_ignore_list_l.addLayout(add_buttons_l)
		self.ignore_path_l = QFormLayout()
		app_ignore_list_l.addLayout(self.ignore_path_l)

		# Web
		web = QTabWidget(self)
		self.web_index = self.right_panel.addWidget(web)

		# Web / Downloader
		web_downloader, web_downloader_l = new_tab('Downloader', web)
		hen_download_group, hen_download_group_l = groupbox('g.e-hentai/exhentai',
													  QFormLayout, web_downloader)
		web_downloader_l.addRow(hen_download_group)
		self.archive_download = QRadioButton('Archive', hen_download_group)
		self.torrent_download = QRadioButton('Torrent', hen_download_group)
		download_type_l = QHBoxLayout()
		download_type_l.addWidget(self.archive_download)
		download_type_l.addWidget(self.torrent_download, 1)
		hen_download_group_l.addRow('Download Type:', download_type_l)
		self.download_directory = PathLineEdit(web_downloader)
		web_downloader_l.addRow('Destination:', self.download_directory)
		self.torrent_client = PathLineEdit(web_downloader, False, '')
		web_downloader_l.addRow(QLabel("Leave empty to use default torrent client."+
								 "\nIt is NOT recommended to import a file while it's still downloading."))
		web_downloader_l.addRow('Torrent client:', self.torrent_client)

		# Web / Metadata
		web_metadata_page = QScrollArea()
		web_metadata_page.setBackgroundRole(QPalette.Base)
		web_metadata_page.setWidgetResizable(True)
		web.addTab(web_metadata_page, 'Metadata')
		web_metadata_dummy = QWidget()
		web_metadata_page.setWidget(web_metadata_dummy)
		web_metadata_m_l = QFormLayout(web_metadata_dummy)
		self.default_ehen_url = QRadioButton('g.e-hentai.org', web_metadata_page)
		self.exhentai_ehen_url = QRadioButton('exhentai.org', web_metadata_page)
		ehen_url_l = QHBoxLayout()
		ehen_url_l.addWidget(self.default_ehen_url)
		ehen_url_l.addWidget(self.exhentai_ehen_url, 1)
		web_metadata_m_l.addRow('Default URL:', ehen_url_l)
		self.continue_a_metadata_fetcher = QCheckBox('Continue from where auto metadata fetcher left off')
		web_metadata_m_l.addRow(self.continue_a_metadata_fetcher)
		self.use_jpn_title = QCheckBox('Use japanese title')
		self.use_jpn_title.setToolTip('Choose the japenese title over the english one')
		web_metadata_m_l.addRow(self.use_jpn_title)
		time_offset_info = QLabel('We need to impose a delay between our requests to avoid getting banned.'+
							' I have made it so you cannot set the delay lower than the recommended (I don\'t'+
							' want you to get banned, anon!).\nSpecify the delay between requests in seconds.')
		time_offset_info.setWordWrap(True)
		self.web_time_offset = QSpinBox()
		self.web_time_offset.setMaximumWidth(40)
		self.web_time_offset.setMinimum(4)
		self.web_time_offset.setMaximum(99)
		web_metadata_m_l.addRow(time_offset_info)
		web_metadata_m_l.addRow('Requests delay in seconds', self.web_time_offset)
		replace_metadata_info = QLabel('When fetching for metadata the new metadata will be appended'+
								 ' to the gallery by default. This means that new data will only be added if'+
								 ' the field was empty. There is however a special case for namespace & tags.'+
								 ' We go through all the new namespace & tags to only add those that'+
								 ' do not already exists.\n\nEnabling this option makes it so that a gallery\'s old data'+
								 ' are deleted and replaced with the new data.')
		replace_metadata_info.setWordWrap(True)
		self.replace_metadata = QCheckBox('Replace old metadata with new metadata')
		web_metadata_m_l.addRow(replace_metadata_info)
		web_metadata_m_l.addRow(self.replace_metadata)
		first_hit_info = QLabel('By default, you get to choose which gallery to extract metadata from when'+
						  ' there is more than one gallery found when searching.\n'+
						  'Enabling this option makes it choose the first hit, saving you from moving your mouse.')
		first_hit_info.setWordWrap(True)
		self.always_first_hit = QCheckBox('Always choose first hit')
		web_metadata_m_l.addRow(first_hit_info)
		web_metadata_m_l.addRow(self.always_first_hit)
		self.use_gallery_link = QCheckBox('Use current gallery link')
		self.use_gallery_link.setToolTip("Metadata will be fetched from the current gallery link"+
								   " if it's a valid ex/g.e gallery url")
		web_metadata_m_l.addRow(self.use_gallery_link)

		# Web / Exhentai
		exhentai_page = QWidget(self)
		web.addTab(exhentai_page, 'ExHentai')
		ipb_layout = QFormLayout()
		exhentai_page.setLayout(ipb_layout)
		self.ipbid_edit = QLineEdit()
		self.ipbpass_edit = QLineEdit()
		exh_tutorial = QLabel(app_constants.EXHEN_COOKIE_TUTORIAL)
		exh_tutorial.setTextFormat(Qt.RichText)
		ipb_layout.addRow('IPB Member ID:', self.ipbid_edit)
		ipb_layout.addRow('IPB Pass Hash:', self.ipbpass_edit)
		ipb_layout.addRow(exh_tutorial)

		# Visual
		visual = QTabWidget(self)
		self.visual_index = self.right_panel.addWidget(visual)
		visual_general_page = QWidget()
		visual.addTab(visual_general_page, 'General')

		grid_view_general_page = QWidget()
		visual.addTab(grid_view_general_page, 'Grid View')
		grid_view_layout = QVBoxLayout()
		grid_view_layout.addWidget(QLabel('Options marked with * requires application restart'),
						   0, Qt.AlignTop)
		grid_view_general_page.setLayout(grid_view_layout)
		# grid view
		# grid view / tooltip
		self.grid_tooltip_group = QGroupBox('Tooltip', grid_view_general_page)
		self.grid_tooltip_group.setCheckable(True)
		grid_view_layout.addWidget(self.grid_tooltip_group, 0, Qt.AlignTop)
		grid_tooltip_layout = QFormLayout()
		self.grid_tooltip_group.setLayout(grid_tooltip_layout)
		grid_tooltip_layout.addRow(QLabel('Control what is'+
									' displayed in the tooltip when hovering a gallery'))
		grid_tooltips_hlayout = FlowLayout()
		grid_tooltip_layout.addRow(grid_tooltips_hlayout)
		self.visual_grid_tooltip_title = QCheckBox('Title')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_title)
		self.visual_grid_tooltip_author = QCheckBox('Author')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_author)
		self.visual_grid_tooltip_chapters = QCheckBox('Chapters')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_chapters)
		self.visual_grid_tooltip_status = QCheckBox('Status')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_status)
		self.visual_grid_tooltip_type = QCheckBox('Type')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_type)
		self.visual_grid_tooltip_lang = QCheckBox('Language')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_lang)
		self.visual_grid_tooltip_descr = QCheckBox('Description')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_descr)
		self.visual_grid_tooltip_tags = QCheckBox('Tags')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_tags)
		self.visual_grid_tooltip_last_read = QCheckBox('Last read')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_last_read)
		self.visual_grid_tooltip_times_read = QCheckBox('Times read')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_times_read)
		self.visual_grid_tooltip_pub_date = QCheckBox('Publication Date')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_pub_date)
		self.visual_grid_tooltip_date_added = QCheckBox('Date added')
		grid_tooltips_hlayout.addWidget(self.visual_grid_tooltip_date_added)
		# grid view / gallery
		grid_gallery_group = QGroupBox('Gallery', grid_view_general_page)
		grid_view_layout.addWidget(grid_gallery_group, 0, Qt.AlignTop)
		grid_gallery_main_l = QFormLayout()
		grid_gallery_main_l.setFormAlignment(Qt.AlignLeft)
		grid_gallery_group.setLayout(grid_gallery_main_l)
		grid_gallery_display = FlowLayout()
		grid_gallery_main_l.addRow('Display icon on gallery:', grid_gallery_display)
		self.external_viewer_ico = QCheckBox('External Viewer')
		grid_gallery_display.addWidget(self.external_viewer_ico)
		self.gallery_type_ico = QCheckBox('File Type')
		grid_gallery_display.addWidget(self.gallery_type_ico)
		if sys.platform.startswith('darwin'):
			grid_gallery_group.setEnabled(False)
		gallery_text_mode = QWidget()
		grid_gallery_main_l.addRow('Text Mode:', gallery_text_mode)
		gallery_text_mode_l = QHBoxLayout()
		gallery_text_mode.setLayout(gallery_text_mode_l)
		self.gallery_text_elide = QRadioButton('Elide text', gallery_text_mode)
		self.gallery_text_fit = QRadioButton('Fit text', gallery_text_mode)
		gallery_text_mode_l.addWidget(self.gallery_text_elide, 0, Qt.AlignLeft)
		gallery_text_mode_l.addWidget(self.gallery_text_fit, 0, Qt.AlignLeft)
		gallery_text_mode_l.addWidget(Spacer('h'), 1, Qt.AlignLeft)
		gallery_font = QHBoxLayout()
		grid_gallery_main_l.addRow('Font:*', gallery_font)
		self.font_lbl = QLabel()
		self.font_size_lbl = QSpinBox()
		self.font_size_lbl.setMaximum(100)
		self.font_size_lbl.setMinimum(1)
		self.font_size_lbl.setToolTip('Font size in pixels')
		choose_font = QPushButton('Choose font')
		choose_font.clicked.connect(self.choose_font)
		gallery_font.addWidget(self.font_lbl, 0, Qt.AlignLeft)
		gallery_font.addWidget(self.font_size_lbl, 0, Qt.AlignLeft)
		gallery_font.addWidget(choose_font, 0, Qt.AlignLeft)
		gallery_font.addWidget(Spacer('h'), 1, Qt.AlignLeft)
		# grid view / colors
		grid_colors_group = QGroupBox('Colors', grid_view_general_page)
		grid_view_layout.addWidget(grid_colors_group, 1, Qt.AlignTop)
		grid_colors_l = QFormLayout()
		grid_colors_group.setLayout(grid_colors_l)
		def color_lineedit():
			l = QLineEdit()
			l.setPlaceholderText('Hex colors. Eg.: #323232')
			l.setMaximumWidth(200)
			return l
		self.grid_label_color = color_lineedit()
		self.grid_title_color = color_lineedit()
		self.grid_artist_color = color_lineedit()
		grid_colors_l.addRow('Label color:', self.grid_label_color)
		grid_colors_l.addRow('Title color:', self.grid_title_color)
		grid_colors_l.addRow('Artist color:', self.grid_artist_color)

		style_page = QWidget(self)
		visual.addTab(style_page, 'Style')
		visual.setTabEnabled(0, False)
		visual.setTabEnabled(2, False)
		visual.setCurrentIndex(1)

		# Advanced
		advanced = QTabWidget(self)
		self.advanced_index = self.right_panel.addWidget(advanced)
		advanced_misc_scroll = QScrollArea(self)
		advanced_misc_scroll.setBackgroundRole(QPalette.Base)
		advanced_misc_scroll.setWidgetResizable(True)
		advanced_misc = QWidget()
		advanced_misc_scroll.setWidget(advanced_misc)
		advanced.addTab(advanced_misc_scroll, 'Misc')
		advanced_misc_main_layout = QVBoxLayout()
		advanced_misc.setLayout(advanced_misc_main_layout)
		misc_controls_layout = QFormLayout()
		advanced_misc_main_layout.addLayout(misc_controls_layout)
		# Advanced / Misc / Grid View
		misc_gridview = QGroupBox('Grid View')
		misc_controls_layout.addWidget(misc_gridview)
		misc_gridview_layout = QFormLayout()
		misc_gridview.setLayout(misc_gridview_layout)
		# Advanced / Misc / Grid View / scroll speed
		scroll_speed_spin_box = QSpinBox()
		scroll_speed_spin_box.setFixedWidth(60)
		scroll_speed_spin_box.setToolTip('Control the speed when scrolling in'+
								   ' grid view. DEFAULT: 7')
		scroll_speed_spin_box.setValue(self.scroll_speed)
		def scroll_speed(v): self.scroll_speed = v
		scroll_speed_spin_box.valueChanged[int].connect(scroll_speed)
		misc_gridview_layout.addRow('Scroll speed:', scroll_speed_spin_box)
		# Advanced / Misc / Grid View / cache size
		cache_size_spin_box = QSpinBox()
		cache_size_spin_box.setFixedWidth(120)
		cache_size_spin_box.setMaximum(999999999)
		cache_size_spin_box.setToolTip('This can greatly reduce lags/freezes in the grid view.' +
								 ' Increase the value if you experience lag when scrolling'+
								 ' through galleries. DEFAULT: 200 MiB')
		def cache_size(c): self.cache_size = (self.cache_size[0], c)
		cache_size_spin_box.setValue(self.cache_size[1])
		cache_size_spin_box.valueChanged[int].connect(cache_size)
		misc_gridview_layout.addRow('Cache Size (MiB):', cache_size_spin_box)

		# Advanced / Gallery
		advanced_gallery, advanced_gallery_m_l = new_tab('Gallery', advanced)
		def rebuild_thumbs():
			confirm_msg = QMessageBox(QMessageBox.Question, '', 'Are you sure you want to regenerate your thumbnails.',
							 QMessageBox.Yes | QMessageBox.No, self)
			if confirm_msg.exec() == QMessageBox.Yes:
				clear_cache_confirm = QMessageBox(QMessageBox.Question, '',
									  'Do you want to delete all old thumbnails before regenerating?', QMessageBox.Yes | QMessageBox.No,
									  self)
				clear_cache = False
				if clear_cache_confirm.exec() == QMessageBox.Yes:
					clear_cache = True
				gallerydb.DatabaseEmitter.RUN = False
				def start_db_activity(): gallerydb.DatabaseEmitter.RUN = True
				app_popup = ApplicationPopup(self.parent_widget)
				app_popup.info_lbl.setText("Regenerating thumbnails...")
				app_popup.admin_db = gallerydb.AdminDB()
				app_popup.admin_db.moveToThread(app_constants.GENERAL_THREAD)
				app_popup.admin_db.DONE.connect(app_popup.admin_db.deleteLater)
				app_popup.admin_db.DONE.connect(start_db_activity)
				app_popup.admin_db.DATA_COUNT.connect(app_popup.prog.setMaximum)
				app_popup.admin_db.PROGRESS.connect(app_popup.prog.setValue)
				self.init_gallery_rebuild.connect(app_popup.admin_db.rebuild_thumbs)
				app_popup.adjustSize()
				self.init_gallery_rebuild.emit(clear_cache)
				app_popup.show()

		rebuild_thumbs_info = QLabel("Clears thumbnail cache and rebuilds it, which can take a while. Tip: Useful when changing thumbnail size.")
		rebuild_thumbs_btn = QPushButton('Regenerate Thumbnails')
		rebuild_thumbs_btn.adjustSize()
		rebuild_thumbs_btn.setFixedWidth(rebuild_thumbs_btn.width())
		rebuild_thumbs_btn.clicked.connect(rebuild_thumbs)
		advanced_gallery_m_l.addRow(rebuild_thumbs_info)
		advanced_gallery_m_l.addRow(rebuild_thumbs_btn)
		g_data_fixer_group, g_data_fixer_l =  groupbox('Gallery Renamer', QFormLayout, advanced_gallery)
		g_data_fixer_group.setEnabled(False)
		advanced_gallery_m_l.addRow(g_data_fixer_group)
		g_data_regex_fix_lbl = QLabel("Rename a gallery through regular expression."+
								" A regex cheatsheet is located at About -> Regex Cheatsheet.")
		g_data_regex_fix_lbl.setWordWrap(True)
		g_data_fixer_l.addRow(g_data_regex_fix_lbl)
		self.g_data_regex_fix_edit = QLineEdit()
		self.g_data_regex_fix_edit.setPlaceholderText("Valid regex")
		g_data_fixer_l.addRow('Regex:', self.g_data_regex_fix_edit)
		self.g_data_replace_fix_edit = QLineEdit()
		self.g_data_replace_fix_edit.setPlaceholderText("Leave empty to delete matches")
		g_data_fixer_l.addRow('Replace with:', self.g_data_replace_fix_edit)
		g_data_fixer_options = FlowLayout()
		g_data_fixer_l.addRow(g_data_fixer_options)
		self.g_data_fixer_title = QCheckBox("Title", g_data_fixer_group)
		self.g_data_fixer_artist = QCheckBox("Artist", g_data_fixer_group)
		g_data_fixer_options.addWidget(self.g_data_fixer_title)
		g_data_fixer_options.addWidget(self.g_data_fixer_artist)

		# Advanced / Database
		advanced_db_page, advanced_db_page_l = new_tab('Database', advanced)
		# Advanced / Database / Import/Export
		def init_export():
			confirm_msg = QMessageBox(QMessageBox.Question, '', 'Are you sure you want to export your database? This might take a long time.',
							 QMessageBox.Yes | QMessageBox.No, self)
			if confirm_msg.exec() == QMessageBox.Yes:
				app_popup = ApplicationPopup(self.parent_widget)
				app_popup.info_lbl.setText("Exporting database...")
				app_popup.export_instance = io_misc.ImportExport()
				app_popup.export_instance.moveToThread(app_constants.GENERAL_THREAD)
				app_popup.export_instance.finished.connect(app_popup.export_instance.deleteLater)
				app_popup.export_instance.finished.connect(app_popup.close)
				app_popup.export_instance.amount.connect(app_popup.prog.setMaximum)
				app_popup.export_instance.progress.connect(app_popup.prog.setValue)
				self.init_gallery_eximport.connect(app_popup.export_instance.export_data)
				self.init_gallery_eximport.emit(None)
				app_popup.adjustSize()
				app_popup.show()
				self.close()

		def init_import():
			path = QFileDialog.getOpenFileName(self,
									  'Choose happypanda database file', filter='*.hpdb')
			path = path[0]
			if len(path) != 0:
				app_popup = ApplicationPopup(self.parent_widget)
				app_popup.restart_info.hide()
				app_popup.info_lbl.setText("Importing database file...")
				app_popup.note_info.setText("Application requires a restart after importing")
				app_popup.import_instance = io_misc.ImportExport()
				app_popup.import_instance.moveToThread(app_constants.GENERAL_THREAD)
				app_popup.import_instance.finished.connect(app_popup.import_instance.deleteLater)
				app_popup.import_instance.finished.connect(app_popup.init_restart)
				app_popup.import_instance.amount.connect(app_popup.prog.setMaximum)
				app_popup.import_instance.imported_g.connect(app_popup.info_lbl.setText)
				app_popup.import_instance.progress.connect(app_popup.prog.setValue)
				self.init_gallery_eximport.connect(app_popup.import_instance.import_data)
				self.init_gallery_eximport.emit(path)
				app_popup.adjustSize()
				app_popup.show()
				self.close()

		advanced_impexp, advanced_impexp_l = groupbox('Import/Export', QFormLayout, advanced_db_page)
		advanced_db_page_l.addRow(advanced_impexp)
		self.export_format = QComboBox(advanced_db_page)
		#self.export_format.addItem('Text File', 0)
		self.export_format.addItem('HPDB', 1)
		self.export_format.adjustSize()
		self.export_format.setFixedWidth(self.export_format.width())
		advanced_impexp_l.addRow('Export Format:', self.export_format)
		self.export_path = PathLineEdit(advanced_impexp, filters='')
		advanced_impexp_l.addRow('Export Path:', self.export_path)
		import_btn = QPushButton('Import database')
		import_btn.clicked.connect(init_import)
		export_btn = QPushButton('Export database')
		export_btn.clicked.connect(init_export)
		ex_imp_btn_l = QHBoxLayout()
		ex_imp_btn_l.addWidget(import_btn)
		ex_imp_btn_l.addWidget(export_btn)
		advanced_impexp_l.addRow(ex_imp_btn_l)


		# About
		about = QTabWidget(self)
		self.about_index = self.right_panel.addWidget(about)
		about_happypanda_page, about_layout = new_tab("About Happypanda", about, False)
		info_lbl = QLabel(app_constants.ABOUT)
		info_lbl.setWordWrap(True)
		info_lbl.setOpenExternalLinks(True)
		about_layout.addWidget(info_lbl)
		about_layout.addWidget(Spacer('v'))
		open_hp_folder = QPushButton('Open Happypanda Directory')
		open_hp_folder.clicked.connect(self.open_hp_folder)
		open_hp_folder.adjustSize()
		open_hp_folder.setFixedWidth(open_hp_folder.width())
		about_layout.addWidget(open_hp_folder)

		# About / DB Overview
		about_db_overview, about_db_overview_m_l = new_tab('DB Overview', about)
		about_stats_tab_widget = misc_db.DBOverview(self.parent_widget)
		about_db_overview_options = QHBoxLayout()
		self.tags_treeview_on_start = QCheckBox('Start with application', about_db_overview)
		make_window_btn = QPushButton('Open in window', about_db_overview)
		make_window_btn.adjustSize()
		make_window_btn.setFixedWidth(make_window_btn.width())
		about_db_overview_options.addWidget(self.tags_treeview_on_start)
		about_db_overview_options.addWidget(make_window_btn)
		def mk_btn_false():
			try:
				make_window_btn.setDisabled(False)
			except RuntimeError:
				pass
		def make_tags_treeview_window():
			self.parent_widget.tags_treeview = misc_db.DBOverview(self.parent_widget, True)
			self.parent_widget.tags_treeview.about_to_close.connect(mk_btn_false)
			make_window_btn.setDisabled(True)
			self.parent_widget.tags_treeview.show()
		if self.parent_widget.tags_treeview:
			self.parent_widget.tags_treeview.about_to_close.connect(mk_btn_false)
			make_window_btn.setDisabled(True)
		make_window_btn.clicked.connect(make_tags_treeview_window)
		about_db_overview_m_l.addRow(about_db_overview_options)
		about_db_overview_m_l.addRow(about_stats_tab_widget)

		# About / Troubleshooting
		about_troubleshoot_page = QWidget()
		about.addTab(about_troubleshoot_page, 'Bug Reporting')
		troubleshoot_layout = QVBoxLayout()
		about_troubleshoot_page.setLayout(troubleshoot_layout)
		guide_lbl = QLabel(app_constants.TROUBLE_GUIDE)
		guide_lbl.setTextFormat(Qt.RichText)
		guide_lbl.setOpenExternalLinks(True)
		guide_lbl.setWordWrap(True)
		troubleshoot_layout.addWidget(guide_lbl, 0, Qt.AlignTop)
		troubleshoot_layout.addWidget(Spacer('v'))

		# About / Search tutorial
		about_search_tut, about_search_tut_l = new_tab("Search Guide", about, True)
		g_search_lbl = QLabel(app_constants.SEARCH_TUTORIAL_TAGS)
		g_search_lbl.setWordWrap(True)
		about_search_tut_l.addRow(g_search_lbl)

		# About / Regex Cheatsheet
		about_s_regex, about_s_regex_l = new_tab("Regex Cheatsheet", about, True)
		reg_info = QLabel(app_constants.REGEXCHEAT)
		reg_info.setWordWrap(True)
		about_s_regex_l.addRow(reg_info)

	def add_folder_monitor(self, path=''):
		if not isinstance(path, str):
			path = ''
		l_edit = PathLineEdit()
		l_edit.setText(path)
		n = self.folders_layout.rowCount() + 1
		self.folders_layout.addRow('{}'.format(n), l_edit)

	def add_ignore_path(self, path='', dir=True):
		if not isinstance(path, str):
			path = ''
		l_edit = PathLineEdit(dir=dir)
		l_edit.setText(path)
		n = self.ignore_path_l.rowCount() + 1
		self.ignore_path_l.addRow('{}'.format(n), l_edit)

	def color_checker(self, txt):
		allow = False
		if len(txt) == 7:
			if txt[0] == '#':
				allow = True
		return allow

	def take_all_layout_widgets(self, l):
		n = l.rowCount()
		items = []
		for x in range(n):
			item = l.takeAt(x+1)
			items.append(item.widget())
		return items


	def choose_font(self):
		tup = QFontDialog.getFont(self)
		font = tup[0]
		if tup[1]:
			self.font_lbl.setText(font.family())
			self.font_size_lbl.setValue(font.pointSize())

	def open_hp_folder(self):
		if os.name == 'posix':
			utils.open_path(app_constants.posix_program_dir)
		else:
			utils.open_path(os.getcwd())

	def reject(self):
		self.close()