예제 #1
0
def main(args):
    app = QApplication(args)
    page = QSplitter()
    data = Model(1000, 10, page)
    selections = QItemSelectionModel(data)
    table = QTableView()
    table.setModel(data)
    table.setSelectionModel(selections)
    table.horizontalHeader().setSectionsMovable(True)
    table.verticalHeader().setSectionsMovable(True)
    # Set StaticContents to enable minimal repaints on resizes.
    table.viewport().setAttribute(Qt.WA_StaticContents)
    page.addWidget(table)
    tree = QTreeView()
    tree.setModel(data)
    tree.setSelectionModel(selections)
    tree.setUniformRowHeights(True)
    tree.header().setStretchLastSection(False)
    tree.viewport().setAttribute(Qt.WA_StaticContents)
    # Disable the focus rect to get minimal repaints when scrolling on Mac.
    tree.setAttribute(Qt.WA_MacShowFocusRect, False)
    page.addWidget(tree)
    list = QListView()
    list.setModel(data)
    list.setSelectionModel(selections)
    list.setViewMode(QListView.IconMode)
    list.setSelectionMode(QAbstractItemView.ExtendedSelection)
    list.setAlternatingRowColors(False)
    list.viewport().setAttribute(Qt.WA_StaticContents)
    list.setAttribute(Qt.WA_MacShowFocusRect, False)
    page.addWidget(list)
    page.setWindowIcon(QIcon(images_dir + '/interview.png'))
    page.setWindowTitle("Interview")
    page.show()
    return app.exec_()
예제 #2
0
def main(args):
    app = QApplication(args)
    page = QSplitter()
    data = Model(1000, 10, page)
    selections = QItemSelectionModel(data)
    table = QTableView()
    table.setModel(data)
    table.setSelectionModel(selections)
    table.horizontalHeader().setSectionsMovable(True)
    table.verticalHeader().setSectionsMovable(True)
    # Set StaticContents to enable minimal repaints on resizes.
    table.viewport().setAttribute(Qt.WA_StaticContents)
    page.addWidget(table)
    tree = QTreeView()
    tree.setModel(data)
    tree.setSelectionModel(selections)
    tree.setUniformRowHeights(True)
    tree.header().setStretchLastSection(False)
    tree.viewport().setAttribute(Qt.WA_StaticContents)
    # Disable the focus rect to get minimal repaints when scrolling on Mac.
    tree.setAttribute(Qt.WA_MacShowFocusRect, False)
    page.addWidget(tree)
    list = QListView()
    list.setModel(data)
    list.setSelectionModel(selections)
    list.setViewMode(QListView.IconMode)
    list.setSelectionMode(QAbstractItemView.ExtendedSelection)
    list.setAlternatingRowColors(False)
    list.viewport().setAttribute(Qt.WA_StaticContents)
    list.setAttribute(Qt.WA_MacShowFocusRect, False)
    page.addWidget(list)
    page.setWindowIcon(QIcon(images_dir + "/interview.png"))
    page.setWindowTitle("Interview")
    page.show()
    return app.exec_()
예제 #3
0
    def __init__(self):
        super().__init__()

        self.setWindowTitle('GIMZoomer')
        self.root_path = os.path.expanduser('~')

        model_filter = QDir.Dirs | QDir.NoDotAndDotDot | QDir.NoSymLinks

        # model = QDirModel()
        # model = QFileSystemModel()
        model = CheckableFileSystemModel()
        model.setRootPath(self.root_path)
        model.setFilter(model_filter)

        tree = QTreeView()
        tree.setModel(model)
        tree.setRootIndex(model.index(self.root_path))
        tree.header().setSectionResizeMode(0, QHeaderView.ResizeToContents)

        grid = QGridLayout()
        grid.addWidget(tree, 0, 0, 1, 1)

        self.setLayout(grid)
        self.resize(640, 480)
        self.show()
예제 #4
0
    def __init__(self, colorModel, parent = None):

        QDockWidget.__init__(self, "&Colors", parent)

        self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        self.colorModel = colorModel
        self.colorPalette = ColorPalette(self)

        colorList = QTreeView()
        colorList.header().hide()
        colorList.setRootIsDecorated(False)
        colorList.setModel(self.colorModel)

        # TODO SIGNAL
        # self.connect(colorList, SIGNAL("activated(QModelIndex)"),
        #              self.editColor)

        self.backgroundButton = QPushButton("&Background Color")
        # TODO SIGNAL
        # self.connect(self.backgroundButton, SIGNAL("clicked()"), self.selectBackground)
        self._set_background_button_color(colorModel.background)

        widget = QWidget()
        layout = QVBoxLayout(widget)
        layout.addWidget(colorList)
        layout.addWidget(self.backgroundButton)

        self.setWidget(widget)
예제 #5
0
class FileBrowserWidget(QWidget):

    def __init__(self):
        super().__init__()
        self.initUI()
    
    def initUI(self):        
        self.model = QFileSystemModel()
        self.model.setRootPath('')
        self.tree = QTreeView()
        self.tree.setModel(self.model)

        idx = self.model.index(BASE_DIR)
        self.tree.setRootIndex(idx)
        
        self.tree.setAnimated(False)
        self.tree.setIndentation(20)
        self.tree.setSortingEnabled(True)
        
        self.tree.hideColumn(2)
        self.tree.header().resizeSection(0,200)
        
        windowLayout = QVBoxLayout()
        windowLayout.addWidget(self.tree)
        self.setLayout(windowLayout)      
예제 #6
0
 def init_view(self):
     """
     Init the view attached to the history.
     """
     self.init_model()
     view = QTreeView()
     view.setHeaderHidden(True)
     view.setSelectionBehavior(QAbstractItemView.SelectItems)
     view.setModel(self.model())
     view.pressed.connect(self.dealWithPressEvent)
     view.header().setStretchLastSection(False)
     self.setView(view)
    def __init__(self, parent=None) -> None:
        QWidget.__init__(self, parent)

        # Основные компоненты
        # Вывод варианта моделирования в виде иерархического дерева
        variant_tree_view = QTreeView()
        # Не хотим пользователю дать разрешение на его редактирование
        variant_tree_view.setEditTriggers(QAbstractItemView.NoEditTriggers)
        # Ссылка на экземпляр класса, управляющего заголовком
        header = variant_tree_view.header()
        # Задание режима для изменения размеров секций (автоматически растягивается)
        header.setSectionResizeMode(QHeaderView.ResizeToContents)
        # Модель представления данных
        self.model = QStandardItemModel(self)
        variant_tree_view.setModel(self.model)

        # Групбокс с вариантом моделирования
        group_box = QGroupBox("Получившийся вариант моделирования")
        group_box_layout = QVBoxLayout(group_box)
        group_box_layout.addWidget(variant_tree_view)
        # Контейнер с кнопками назад/далее
        control_layout = LayoutWithBackAndNextButtons()

        # Основной контейнер
        main_layout = QVBoxLayout(self)
        main_layout.addWidget(group_box)
        main_layout.addLayout(control_layout)
        # Для удобного доступа
        self.save_file_button = control_layout.next_button
        self.back_button = control_layout.back_button
        # Изменим надпись на кнопке на подходящую
        self.save_file_button.setText("Сохранить вариант моделирования")
예제 #8
0
class FileTree(QWidget):

    def __init__(self, defaultfolder=r'c:\Zen_Output'):
        super(QWidget, self).__init__()

        filter = ['*.czi', '*.ome.tiff', '*ome.tif' '*.tiff' '*.tif']

        # define the style for the FileTree via s style sheet
        self.setStyleSheet("""
            QTreeView::item {
            background-color: rgb(38, 41, 48);
            font-weight: bold;
            }

            QTreeView::item::selected {
            background-color: rgb(38, 41, 48);
            color: rgb(0, 255, 0);

            }

            QTreeView QHeaderView:section {
            background-color: rgb(38, 41, 48);
            color: rgb(255, 255, 255);
            }
            """)

        self.model = QFileSystemModel()
        self.model.setRootPath(defaultfolder)
        self.model.setFilter(QtCore.QDir.AllDirs | QDir.Files | QtCore.QDir.NoDotAndDotDot)
        self.model.setNameFilterDisables(False)
        self.model.setNameFilters(filter)

        self.tree = QTreeView()
        self.tree.setModel(self.model)
        self.tree.setRootIndex(self.model.index(defaultfolder))
        self.tree.setAnimated(True)
        self.tree.setIndentation(20)
        self.tree.setSortingEnabled(False)
        header = self.tree.header()
        header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)

        windowLayout = QVBoxLayout()
        windowLayout.addWidget(self.tree)
        self.setLayout(windowLayout)

        self.tree.clicked.connect(self.on_treeView_clicked)

    @pyqtSlot()
    def on_treeView_clicked(self, index):
        indexItem = self.model.index(index.row(), 0, index.parent())
        filename = self.model.fileName(indexItem)
        filepath = self.model.filePath(indexItem)

        # open the file when clicked
        print('Opening ImageFile : ', filepath)
        open_image_stack(filepath)
예제 #9
0
class ReqTreeView(QWidget):
    def __init__(self):
        QWidget.__init__(self)

        self.setLayout(QVBoxLayout())
        self.layout().setSpacing(0)
        self.layout().setContentsMargins(0, 0, 0, 0)

        self.nodes = {}
        self.tree_view = QTreeView()
        self.tree_view.header().close()
        self.root = QStandardItemModel()
        self.tree_view.setModel(self.root)
        self.layout().addWidget(self.tree_view)

    @pyqtSlot(HTTPRequest)
    def add_request_item(self, req):
        path_parts = req.url.geturl(False).split("/")
        path_parts = path_parts[1:]
        path_parts = ["/" + p for p in path_parts]
        path_parts = [req.dest_host] + path_parts
        if path_parts[0] not in self.nodes:
            item = PathNodeItem(path_parts[0], path_parts[0])
            item.setFlags(item.flags() ^ Qt.ItemIsEditable)
            self.nodes[path_parts[0]] = item
            self.root.appendRow(item)
        else:
            item = self.nodes[path_parts[0]]
        item.add_child_path(path_parts[1:])

    @pyqtSlot(list)
    def set_requests(self, reqs):
        self.clear()
        for req in reqs:
            if _include_req(req):
                self.add_request_item(req)
        self.tree_view.expandAll()

    def clear(self):
        self.nodes = {}
        self.root = QStandardItemModel()
        self.tree_view.setModel(self.root)
예제 #10
0
class RouteTable(QWidget):
    NO1, FROM, NO2, TO, CLUSTER = range(5)

    def __init__(self):
        super().__init__()
        # Main layout
        main_layout = QVBoxLayout()

        # Sub layout
        self.cluster_combobox = QComboBox()
        self.table_container = QGroupBox("Result")

        # Route Table
        self.table = QTreeView()
        self.table.setRootIsDecorated(False)
        self.table.setAlternatingRowColors(True)

        self.model = QStandardItemModel(0, 5, self)
        self.model.setHeaderData(self.NO1, Qt.Horizontal, "No")
        self.model.setHeaderData(self.FROM, Qt.Horizontal, "From")
        self.model.setHeaderData(self.NO2, Qt.Horizontal, "No")
        self.model.setHeaderData(self.TO, Qt.Horizontal, "To")
        self.model.setHeaderData(self.CLUSTER, Qt.Horizontal, "Cluster number")

        self.table.setModel(self.model)
        table_header = self.table.header()
        table_header.resizeSection(0, 25)
        table_header.resizeSection(1, 190)
        table_header.resizeSection(2, 25)
        table_header.resizeSection(3, 190)

        table_container_layout = QHBoxLayout()
        table_container_layout.addWidget(self.table)
        self.table_container.setLayout(table_container_layout)

        # Add to main layout
        main_layout.addWidget(self.cluster_combobox)
        main_layout.addWidget(self.table_container)
        self.setLayout(main_layout)

    def addRoute(self, model, from_no, from_route, to_no, to_route,
                 cluster_number):
        root = self.table.model()
        n = root.rowCount()
        model.insertRow(n)
        model.setData(model.index(n, self.NO1), from_no)
        model.setData(model.index(n, self.FROM), from_route)
        model.setData(model.index(n, self.NO2), to_no)
        model.setData(model.index(n, self.TO), to_route)
        model.setData(model.index(n, self.CLUSTER), cluster_number)

    def clearAllRoute(self):
        root = self.table.model()
        root.removeRows(0, root.rowCount())
예제 #11
0
    def showVideoInfoDialog(self, outjson):
        """ Show Video Information Dialog """
        view = QTreeView()
        model = QJsonModel()
        view.setModel(model)
        model.loadJsonFromConsole(outjson)

        self.VideoInfoDialog = QDialog(self)
        self.VideoInfoDialog.setWindowTitle("Video Information : " +
                                            self.fileName)
        self.VideoInfoDialog.setWindowIcon(
            QIcon(":/imgFMV/images/video_information.png"))

        self.verticalLayout = QVBoxLayout(self.VideoInfoDialog)
        self.verticalLayout.addWidget(view)
        view.expandAll()
        view.header().setSectionResizeMode(QHeaderView.ResizeToContents)

        self.VideoInfoDialog.setWindowFlags(Qt.Window
                                            | Qt.WindowCloseButtonHint)
        self.VideoInfoDialog.setObjectName("VideoInfoDialog")
        self.VideoInfoDialog.resize(500, 400)
        self.VideoInfoDialog.show()
예제 #12
0
class StacktraceWidget(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.tree = QTreeView(self)
        layout = QVBoxLayout(self)
        layout.addWidget(self.tree)
        self.model = QStandardItemModel()
        self.model.setHorizontalHeaderLabels(["Level", "Values"])
        self.tree.header().setDefaultSectionSize(180)
        self.tree.setModel(self.model)

    def importData(self, data):
        self.model.setRowCount(0)
        root = self.model.invisibleRootItem()
        for index, stack_values in enumerate(data):
            parent = QStandardItem(
                str(stack_values.f_code.co_name)
                + str(
                    inspect.signature(
                        types.FunctionType(stack_values.f_code, {})
                    )
                )
            )
            parent.setEditable(False)
            for key, value in stack_values.f_locals.items():
                keyWidget = QStandardItem(str(key))
                keyWidget.setEditable(False)
                try:
                    value_as_str = repr(value)
                except BaseException:
                    value_as_str = "Can't see"
                valueWidget = ValueWidget(
                    self.parent, value_as_str, str(key), str(index)
                )
                parent.appendRow([keyWidget, valueWidget])
            root.appendRow(parent)
예제 #13
0
class CategoriesWidget(QWidget):
    def __init__(self, categoriesTreeItems):
        super().__init__()
        self.setLayout(QGridLayout())

        self.categoriesTreeItems = categoriesTreeItems

        self.categoriesView = QTreeView()
        self.categoriesView.header().hide()
        self.categoriesModel = TreeModel(self.categoriesTreeItems)

        self.categoriesProxyModel = CategoriesFilterProxy()
        self.categoriesProxyModel.setSourceModel(self.categoriesModel)

        self.categoriesView.setModel(self.categoriesProxyModel)

        self.filterCategoriesEdit = QLineEdit()
        self.filterCategoriesEdit.setPlaceholderText("Filter categories")
        self.categoriesProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.filterCategoriesEdit.textEdited.connect(
            self.categoriesProxyModel.setFilterRegExp)

        self.layout().addWidget(self.filterCategoriesEdit, 0, 1, 1, 1)
        self.layout().addWidget(self.categoriesView, 1, 1, 1, 1)
예제 #14
0
class PYCFScape(QMainWindow):
    def __init__(self):
        super().__init__()

        # We determine where the script is placed, for acessing files we use (such as the icon)
        self.we_live_in = sys.argv[0]
        self.we_live_in = os.path.split(self.we_live_in)[0]

        self.build_interface()

        self.opened_file = None  # stores the filepath
        self.opened_vpk = None  # stores the opened vpk
        self.internal_directory_understanding = {
        }  # a dictionary version of the paths
        self.vpk_loaded = False  # whether or not we have a vpk file open
        self.export_paths = [
        ]  # Paths we want to export when file->export is selected

    def build_interface(self):
        self.setWindowTitle("PYCFScape")
        self.setWindowIcon(
            QIcon(os.path.join(self.we_live_in, 'res/Icon64.ico')))

        self.main_content = QWidget()
        self.main_content_layout = QVBoxLayout()
        self.main_content.setLayout(self.main_content_layout)

        # set up the interface parts
        self.output_console = QTextEdit()  # Will basically just copy stdout
        self.output_console.setReadOnly(True)
        sys.stdout = bob_logger(self.output_console, False, sys.stdout)
        sys.stderr = bob_logger(self.output_console, True, sys.stderr)

        self.vpk_tree = QTreeView()  # Displays the tree of the vpk's content
        self.vpk_tree_model = QStandardItemModel(self.vpk_tree)
        self.vpk_tree.setModel(self.vpk_tree_model)
        self.vpk_tree._mousePressEvent = self.vpk_tree.mousePressEvent  # store it so we can call it
        self.vpk_tree.mousePressEvent = self.vpk_tree_item_clicked

        self.vpk_tree.setHeaderHidden(True)

        # We use a QTreeView to also display headers.
        # We however, still treat it as a list view.
        self.dir_list = QTreeView(
        )  # Displays the list of the current vpk's directory's content
        self.dir_list_model = QStandardItemModel(self.dir_list)
        self.dir_list.setModel(self.dir_list_model)
        self.dir_list.doubleClicked.connect(self.dir_list_item_double_clicked)
        self.dir_list_model.itemChanged.connect(self.dir_list_item_updated)
        self.dir_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.dir_list.customContextMenuRequested.connect(
            self.dir_list_context_menu)

        self.dir_list_model.setColumnCount(2)
        self.dir_list_model.setHorizontalHeaderLabels(["Name", "Type"])
        self.dir_list.header().resizeSection(0, 250)

        # The tool bar - WARNING: MESSY CODE!
        self.actions_toolbar = self.addToolBar("")
        # OPEN BUTTON
        self.open_button = self.actions_toolbar.addAction(
            QIcon.fromTheme("document-open"), "Open File")
        self.open_button.triggered.connect(self.open_file)

        self.actions_toolbar.addSeparator()

        self.back_button = QToolButton()  # GO BACK BUTTON
        self.back_button.setIcon(QIcon.fromTheme("go-previous"))
        self.back_button.setDisabled(True)
        self.actions_toolbar.addWidget(self.back_button)

        self.forward_button = QToolButton()  # GO FORWARD BUTTON
        self.forward_button.setIcon(QIcon.fromTheme("go-next"))
        self.forward_button.setDisabled(True)
        self.actions_toolbar.addWidget(self.forward_button)

        self.up_button = QToolButton()  # GO UP BUTTON
        self.up_button.setIcon(QIcon.fromTheme("go-up"))
        self.up_button.setDisabled(True)
        self.actions_toolbar.addWidget(self.up_button)

        self.actions_toolbar.addSeparator()

        # FIND BUTTON
        self.search_button = self.actions_toolbar.addAction(
            QIcon.fromTheme("system-search"), "Find in file")
        self.search_button.setDisabled(True)
        self.search_button.triggered.connect(self.search_for_file)

        self.actions_toolbar.addSeparator()

        # EXPORT BUTTON
        self.export_button = self.actions_toolbar.addAction(
            QIcon.fromTheme("extract-archive"), "Export Selection")
        self.export_button.setDisabled(False)
        self.export_button.triggered.connect(self.export_selection)

        self.main_content_layout.addWidget(self.actions_toolbar)

        # now we want the menubar
        self.menu_bar = self.menuBar()
        self.file_menu = self.menu_bar.addMenu("&File")
        self.edit_menu = self.menu_bar.addMenu("&Edit")
        self.menu_bar.addSeparator()
        self.help_menu = self.menu_bar.addMenu("&Help")

        self.file_menu.addActions([self.open_button])

        self.close_button = self.file_menu.addAction(
            QIcon.fromTheme("document-close"),
            "Close File"  # bit redundant, i actually see no use
        )
        self.close_button.triggered.connect(self.close_vpk)

        self.file_menu.addSeparator()

        self.file_menu.addAction(QIcon.fromTheme("application-exit"),
                                 "Exit").triggered.connect(self.close)

        self.edit_menu.addActions([self.search_button])

        self.help_menu.addAction(QIcon.fromTheme("help-about"),
                                 "About && License").triggered.connect(
                                     self.about)

        # the statusbar
        self.status_bar = self.statusBar()

        # set up the splitters

        # horizontal
        self.horz_splitter_container = QWidget()
        self.horz_splitter_container_layout = QVBoxLayout()
        self.horz_splitter_container.setLayout(
            self.horz_splitter_container_layout)

        self.horz_splitter = QSplitter(Qt.Horizontal)

        self.horz_splitter.addWidget(self.vpk_tree)
        self.horz_splitter.addWidget(self.dir_list)

        self.horz_splitter.setSizes([50, 200])

        self.horz_splitter_container_layout.addWidget(self.horz_splitter)

        # vertical
        self.vert_splitter = QSplitter(Qt.Vertical)

        self.vert_splitter.addWidget(self.horz_splitter_container)
        self.vert_splitter.addWidget(self.output_console)

        self.vert_splitter.setSizes([200, 50])

        self.main_content_layout.addWidget(self.vert_splitter)
        self.setCentralWidget(self.main_content)

        self.show()

    ##
    # Update Functions
    ##
    def update_console(self, text, is_stdout):
        colour = Qt.Red if not is_stdout else Qt.black
        self.output_console.setTextColor(colour)
        self.output_console.moveCursor(QTextCursor.End)
        self.output_console.insertPlainText(text)

    def update_interface(self):

        # update the tree view
        self.update_vpk_tree()

        # update the list view
        self.update_dir_list()

        self.search_button.setDisabled(not self.vpk_loaded)

    def update_vpk_tree(self):
        self.vpk_tree_model.removeRows(0, self.vpk_tree_model.rowCount())
        if self.opened_vpk:
            self.tree_magic(self.internal_directory_understanding)

    def update_dir_list(self):
        self.dir_list_model.removeRows(0, self.dir_list_model.rowCount(
        ))  # remove anyway (for instances such as opening a new file)

        selected = self.vpk_tree.selectedIndexes()
        if not selected:
            return
        selected = selected[0]
        selected_item = self.vpk_tree_model.itemFromIndex(selected)
        if not selected_item:
            return

        if not selected_item.is_dir:
            return

        path = selected_item.path
        understanding = self.get_understanding_from(
            self.internal_directory_understanding, path)
        self.list_magic(understanding, path)

    ##
    # Events
    ##
    def vpk_tree_item_clicked(self, event):
        self.vpk_tree._mousePressEvent(event)

        index = self.vpk_tree.indexAt(event.pos())

        # We can rather safely assume any items in the vpk tree will have OUR special attributes
        if index.isValid():
            item = self.vpk_tree_model.itemFromIndex(index)
            print("selected", item.path)

            if item.is_dir:
                self.update_dir_list()

    def dir_list_item_double_clicked(self, index):

        item = self.dir_list_model.itemFromIndex(index)
        if not item.column() == 0:
            return
        print("double clicked", item.path)

        if item.is_dir:
            print("is dir")
            # this is probably a REALLY **REALLY** awful way of doing this, but you're welcome to PR a better way. :)
            index_in_tree = self.find_in_model(self.vpk_tree_model, item.path)

            if index_in_tree.isValid():
                self.vpk_tree.setCurrentIndex(index_in_tree)
                self.update_dir_list()
        else:
            self.status_bar.showMessage(MSG_EXPORT)

            # we clearly wanna export the file and open it
            bits = self.opened_vpk[item.path[1:]].read()

            path = compat.write_to_temp(bits, os.path.split(item.path)[1])
            print("Wrote to", path)
            compat.tell_os_open(path)

            self.status_bar.clearMessage()

    def dir_list_item_updated(self, item):
        if item.checkState() == Qt.Checked:
            if not item.is_dir:
                self.export_paths.append(item.path)
            else:
                index_in_tree = self.find_in_model(self.vpk_tree_model,
                                                   item.path)

                if index_in_tree.isValid():
                    paths = self.recursively_get_paths_from_dir_index_item(
                        index_in_tree, self.vpk_tree_model)

                    self.export_paths += paths

        elif item.checkState() == Qt.Unchecked:
            if not item.is_dir:
                if item.path in self.export_paths:
                    self.export_paths.remove(item.path)
            else:
                index_in_tree = self.find_in_model(self.vpk_tree_model,
                                                   item.path)

                if index_in_tree.isValid():
                    paths = self.recursively_get_paths_from_dir_index_item(
                        index_in_tree, self.vpk_tree_model)

                    for path in paths:
                        if path in self.export_paths:
                            self.export_paths.remove(path)

    ##
    # The next 3 functions are the original pathtodir but merged with the program
    ##
    def nested_dict(self):
        return defaultdict(self.nested_dict)

    def nested_dict_to_regular(self, d):
        if not isinstance(d, defaultdict):
            return d
        return {k: self.nested_dict_to_regular(v) for k, v in d.items()}

    def understand_directories(self, list_of_paths):
        use_dict = defaultdict(self.nested_dict)

        for path in list_of_paths:
            parts = path.split('/')
            if parts:
                marcher = use_dict
                for key in parts[:-1]:
                    marcher = marcher[key]
                marcher[parts[-1]] = parts[-1]

        return dict(use_dict)

    def get_understanding_from(self, understanding, path):
        keys = path.split('/')
        if keys[0] == '':
            keys = keys[1:]

        if keys:
            marcher = understanding
            for key in keys:
                marcher = marcher[key]

            # we can now assume marcher is the understanding we want
            return marcher

    ##
    # Utility
    ##
    def tree_magic(self, dict_of_things, parent=None, root=''):

        if not parent:
            parent = self.vpk_tree_model

        # Stack overflow 14478170
        for thing in sorted(dict_of_things, key=lambda f: os.path.splitext(f)):

            path = root + '/{}'.format(thing)

            thing_item = QStandardItem()
            thing_item.setText(thing)
            thing_item.setEditable(False)

            thing_item.path = path
            thing_item.is_dir = False

            icon, _ = self.get_info_from_path(path)

            thing_item.setIcon(icon)

            if isinstance(dict_of_things[thing], dict):
                thing_item.setIcon(QIcon.fromTheme("folder"))
                self.tree_magic(dict_of_things[thing], thing_item, path)

                thing_item.is_dir = True

            parent.appendRow(thing_item)

    def list_magic(self, dict_of_things, root=''):
        # like tree_magic but operates on dir_list

        for thing in sorted(dict_of_things, key=lambda f: os.path.splitext(f)):

            path = root + '/{}'.format(thing)

            thing_item = QStandardItem()
            thing_item.setText(thing)
            thing_item.setEditable(False)
            thing_item.setCheckable(True)

            thing_item_type = QStandardItem(
            )  # This doesn't actually do anything but convey more information to the user
            thing_item_type.setEditable(False)

            if path in self.export_paths:
                thing_item.setCheckState(Qt.Checked)

            thing_item.path = path
            thing_item.is_dir = False

            icon, desc = self.get_info_from_path(path)

            thing_item.setIcon(icon)
            thing_item_type.setText(desc)

            if isinstance(dict_of_things[thing], dict):
                thing_item.setIcon(QIcon.fromTheme("folder"))
                thing_item.is_dir = True
                thing_item_type.setText("Directory")

            self.dir_list_model.appendRow([thing_item, thing_item_type])

    def get_info_from_path(self,
                           path):  # returns the icon AND description string

        icon = None
        desc = None

        # first we test against mimetype
        # probably bad code, but it works!

        thing_mimetype = mimetypes.guess_type(path)[0]

        if thing_mimetype:
            if thing_mimetype[:6] == "audio/":
                icon = QIcon.fromTheme("audio-x-generic")
            elif thing_mimetype[:12] == "application/":
                icon = QIcon.fromTheme("application-x-generic")
            elif thing_mimetype[:5] == "text/":
                icon = QIcon.fromTheme("text-x-generic")
            elif thing_mimetype[:6] == "image/":
                icon = QIcon.fromTheme("image-x-generic")
            elif thing_mimetype[:6] == "video/":
                icon = QIcon.fromTheme("video-x-generic")

            desc = thing_mimetype

        # well ok, maybe that didn't work, let's test the filepath ourselves.

        file_ext = os.path.splitext(path)[1]

        if file_ext:
            if file_ext in [".vtf"]:
                icon = QIcon.fromTheme("image-x-generic")
                desc = "Valve Texture File"
            elif file_ext in [".vmt"]:
                icon = QIcon.fromTheme("text-x-generic")
                desc = "Valve Material File"
            elif file_ext in [
                    ".pcf"
            ]:  # we can safely assume they are not fonts in this context, but rather
                icon = QIcon.fromTheme("text-x-script")
                desc = "Valve DMX Implementation"  # TODO: is there a better name
            elif file_ext in [".bsp"]:
                icon = QIcon.fromTheme("text-x-generic")
                desc = "Binary Space Partition"
            elif file_ext in [".res"]:
                icon = QIcon.fromTheme("text-x-generic")
                desc = "Valve Key Value"
            elif file_ext in [".vcd"]:
                icon = QIcon.fromTheme("text-x-generic")
                desc = "Valve Choreography Data"

        if not icon:  # If all else fails, display SOMETHING
            icon = QIcon.fromTheme("text-x-generic")
        if not desc:
            desc = "File"

        return icon, desc

    def find_in_model(self, model: QStandardItemModel, path):
        for i in range(0, model.rowCount()):
            index_in_tree = model.index(i, 0)

            if model.itemFromIndex(index_in_tree).path == path:
                return index_in_tree

            if model.itemFromIndex(index_in_tree).is_dir:
                index_in_tree = self.find_in_model_parent(model,
                                                          path,
                                                          parent=index_in_tree)

                if not index_in_tree.isValid():
                    continue

                if model.itemFromIndex(index_in_tree).path == path:
                    return index_in_tree

    def find_in_model_parent(self, model: QStandardItemModel, path, parent):
        for i in range(0, model.rowCount(parent)):
            index_in_tree = model.index(i, 0, parent)

            if model.itemFromIndex(index_in_tree).path == path:
                return index_in_tree

            if model.itemFromIndex(index_in_tree).is_dir:
                index_in_tree = self.find_in_model_parent(model,
                                                          path,
                                                          parent=index_in_tree)

                if not index_in_tree.isValid():
                    continue

                if model.itemFromIndex(index_in_tree).path == path:
                    return index_in_tree

        return QModelIndex()

    def export_file(self, path, out_dir):
        filepath = os.path.split(path)[0]

        if not os.path.isdir('{}{}'.format(out_dir, filepath)):
            os.makedirs('{}{}'.format(out_dir, filepath))

        print("Attempting to export to", "{}{}".format(out_dir, filepath),
              "from", path[1:], "in the vpk")
        outcontents = self.opened_vpk.get_file(path[1:]).read()

        outfile = open('{}{}'.format(out_dir, path), 'wb')
        outfile.write(outcontents)
        outfile.close()

    def recursively_get_paths_from_dir_index_item(self, index_in_tree, model):
        paths = []

        for i in range(
                self.vpk_tree_model.itemFromIndex(index_in_tree).rowCount()):
            index = self.vpk_tree_model.index(i, 0, index_in_tree)
            index_item = self.vpk_tree_model.itemFromIndex(index)

            if not index_item.is_dir:
                paths.append(index_item.path)
            else:
                paths += self.recursively_get_paths_from_dir_index_item(
                    index, model)

        return paths

    def close_vpk(self):  # We trash everything!
        self.vpk_loaded = False
        self.opened_file = None
        self.opened_vpk = {}
        self.export_paths = []
        self.internal_directory_understanding = {}

        self.status_bar.showMessage(MSG_UPDATE_UI)
        self.update_interface()
        self.status_bar.clearMessage()

    def open_vpk(self, vpk_path):
        if self.vpk_loaded:  # if we already have a file open, close it.
            self.close_vpk()

        self.status_bar.showMessage(MSG_OPEN_VPK)

        if not os.path.exists(vpk_path):
            print(
                "Attempted to open {}, which doesn't exist.".format(vpk_path))
            return

        self.opened_file = vpk_path

        try:
            self.opened_vpk = vpk.open(vpk_path)
        except Exception as e:
            print("Ran into an error from the VPK Library.")
            sys.stdout.write(str(e))
            self.error_box(str(e))
            return

        self.vpk_loaded = True

        self.status_bar.showMessage(MSG_UNDERSTAND_VPK)

        # Now we attempt to understand the vpk
        self.internal_directory_understanding = self.understand_directories(
            self.opened_vpk)

        self.status_bar.showMessage(MSG_UPDATE_UI)
        self.update_interface()
        self.status_bar.clearMessage()

    ##
    # Dialogs
    ##
    def open_dialog(self):
        fn = QFileDialog.getOpenFileName(None,
                                         "Open Package",
                                         str(pathlib.Path.home()),
                                         filter=("Valve Pak Files (*.vpk)"))

        return fn

    def open_dir_dialog(self, title="Open Directory"):
        fn = QFileDialog.getExistingDirectory(None, title,
                                              str(pathlib.Path.home()))

        return fn

    def error_box(self, text="...", title="Error"):
        box = QMessageBox()
        box.setIcon(QMessageBox.Critical)
        box.setText(text)
        box.setWindowTitle(title)
        box.setStandardButtons(QMessageBox.Ok)

        return box.exec()

    def info_box(self, text="...", title="Info"):
        box = QMessageBox()
        box.setIcon(QMessageBox.Information)
        box.setText(text)
        box.setWindowTitle(title)
        box.setStandardButtons(QMessageBox.Ok)

        return box.exec()

    def dir_list_context_menu(self, event):
        menu = QMenu(self)

        selected = self.dir_list.selectedIndexes()
        if not selected:
            return
        selected = selected[0]
        selected_item = self.dir_list_model.itemFromIndex(selected)
        path = selected_item.path

        extract = menu.addAction(QIcon.fromTheme("extract-archive"), "Extract")
        validate = menu.addAction(QIcon.fromTheme("view-certificate"),
                                  "Validate")  # TODO: better validation icon
        menu.addSeparator()
        gotodirectory = menu.addAction(QIcon.fromTheme("folder"),
                                       "Go To Directory")
        menu.addSeparator()
        properties = menu.addAction(QIcon.fromTheme("settings-configure"),
                                    "Properties")

        extract.setDisabled(selected_item.is_dir)
        validate.setDisabled(selected_item.is_dir)

        action = menu.exec_(self.dir_list.mapToGlobal(event))
        if action == extract:
            self.export_selected_file(path)
        elif action == validate:
            self.validate_file(path)
        elif action in [gotodirectory, properties]:
            self.info_box(
                "I'm not sure what this does in the original GCFScape.\nIf you know, please make an issue on github!"
            )

    def about(self):
        box = QMessageBox()
        box.setWindowTitle(
            "About PYCFScape")  # TODO: what do we version and how
        box.setText("""PYCFScape
Version 0
MIT LICENSE V1.00 OR LATER
Python {}
QT {}
AUTHORS (Current Version):
ACBob - https://acbob.gitlab.io

Project Homepage
https://github.com/acbob/pycfscape""".format(sys.version, QT_VERSION_STR))

        box.setIcon(QMessageBox.Information)

        box.exec()

    ##
    # Actions
    ##
    def open_file(self):
        self.status_bar.showMessage(MSG_USER_WAIT)

        fn = self.open_dialog()[0]
        if not fn:
            self.status_bar.clearMessage()
            return

        self.open_vpk(fn)

    def search_for_file(self, event):
        print(event)

    def export_selection(self):
        if not self.export_paths:
            self.info_box(
                "You can't export nothing!\n(Please select some items to export.)"
            )
            return

        self.status_bar.showMessage(MSG_USER_WAIT)
        output_dir = self.open_dir_dialog("Export to...")

        if output_dir:
            self.status_bar.showMessage(MSG_EXPORT)
            print("attempting export to", output_dir)

            for file in self.export_paths:
                self.export_file(file, output_dir)

        self.status_bar.clearMessage()

    def export_selected_file(self, file):
        self.status_bar.showMessage(MSG_USER_WAIT)
        output_dir = self.open_dir_dialog("Export to...")

        if output_dir:
            self.status_bar.showMessage(MSG_EXPORT)
            print("attempting export to", output_dir)

            self.export_file(file, output_dir)

        self.status_bar.clearMessage()

    def validate_file(self, file):
        filetoverify = self.opened_vpk.get_file(file[1:])

        if filetoverify:
            verified = filetoverify.verify()
            if verified:
                self.info_box("{} is a perfectly healthy file.".format(file),
                              "All's good.")
            else:
                self.error_box("{} is not valid!".format(file), "Uh oh.")
        else:
            print("What? file doesn't exist? HOW IS THIS MAN")
예제 #15
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self._app = QApplication.instance()
        self._lineEdit = None
        self.create_menu()
        self._completer = TreeModelCompleter(None, self)
        self._completer.setModel(self.model_from_file("./treemodel.txt"))
        self._completer.set_separator(".")
        self._completer.highlighted[QModelIndex].connect(self.highlight)

        central_widget = QWidget()
        model_label = QLabel()
        model_label.setText("Tree Model<br>(Double click items to edit)")
        mode_label = QLabel()
        mode_label.setText("Completion Mode")
        self._mode_combo = QComboBox()
        self._mode_combo.addItem(tr("Inline"))
        self._mode_combo.addItem(tr("Filtered Popup"))
        self._mode_combo.addItem(tr("Unfiltered Popup"))
        self._mode_combo.setCurrentIndex(1)

        case_label = QLabel()
        case_label.setText(tr("Case Sensitivity"))
        self._case_combo = QComboBox()
        self._case_combo.addItem(tr("Case Insensitive"))
        self._case_combo.addItem(tr("Case Sensitive"))
        self._case_combo.setCurrentIndex(0)

        separator_label = QLabel()
        separator_label.setText(tr("Tree Separator"))

        separator_line_edit = QLineEdit()
        separator_line_edit.setText(self._completer.separator())
        separator_line_edit.textChanged.connect(self._completer.set_separator)

        wrap_check_box = QCheckBox()
        wrap_check_box.setText(tr("Wrap around completions"))
        wrap_check_box.setChecked(self._completer.wrapAround())
        wrap_check_box.clicked.connect(self._completer.setWrapAround)

        self._contents_label = QLabel()
        self._contents_label.setSizePolicy(QSizePolicy.Fixed,
                                           QSizePolicy.Fixed)
        separator_line_edit.textChanged.connect(self.update_contents_label)

        self._tree_view = QTreeView()
        self._tree_view.setModel(self._completer.model())
        self._tree_view.header().hide()
        self._tree_view.expandAll()

        self._mode_combo.activated.connect(self.change_mode)
        self._case_combo.activated.connect(self.change_case)

        self._line_edit = QLineEdit()
        self._line_edit.setCompleter(self._completer)

        layout = QGridLayout()
        layout.addWidget(model_label, 0,
                         0), layout.addWidget(self._tree_view, 0, 1)
        layout.addWidget(mode_label, 1,
                         0), layout.addWidget(self._mode_combo, 1, 1)
        layout.addWidget(case_label, 2,
                         0), layout.addWidget(self._case_combo, 2, 1)
        layout.addWidget(separator_label, 3,
                         0), layout.addWidget(separator_line_edit, 3, 1)
        layout.addWidget(wrap_check_box, 4, 0)
        layout.addWidget(self._contents_label, 5, 0, 1, 2)
        layout.addWidget(self._line_edit, 6, 0, 1, 2)
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

        self.change_case(self._case_combo.currentIndex())
        self.change_mode(self._mode_combo.currentIndex())

        self.setWindowTitle(tr("Tree Model Completer"))
        self._line_edit.setFocus()

    def create_menu(self):
        exit_action = QAction(tr("Exit"), self)
        about_act = QAction(tr("About"), self)
        about_qt_act = QAction(tr("About Qt"), self)

        exit_action.triggered.connect(QApplication.instance().quit)
        about_act.triggered.connect(self.about)
        about_qt_act.triggered.connect(QApplication.instance().aboutQt)

        file_menu = self.menuBar().addMenu(tr("File"))
        file_menu.addAction(exit_action)

        help_menu = self.menuBar().addMenu(tr("About"))
        help_menu.addAction(about_act)
        help_menu.addAction(about_qt_act)

    def change_mode(self, index):
        modes = (QCompleter.InlineCompletion, QCompleter.PopupCompletion,
                 QCompleter.UnfilteredPopupCompletion)
        self._completer.setCompletionMode(modes[index])

    def model_from_file(self, file_name):
        # file = QFile(file_name)
        # if not file.open(QFile.ReadOnly):
        #     return QStringListModel(self._completer)
        QApplication.instance().setOverrideCursor(QCursor(Qt.WaitCursor))
        model = QStandardItemModel(self._completer)
        parents = [model.invisibleRootItem()]

        with open(file_name) as file:
            pat = re.compile("^\\s+")
            for line in file:
                if not line:
                    continue
                trimmed_line = line.strip()
                if not trimmed_line:
                    continue
                match = pat.match(line)
                if not match:
                    level = 0
                else:
                    length = match.end() - match.start()
                    if line.startswith("\t"):
                        level = length
                    else:
                        level = length // 4

                while len(parents) < level + 2:
                    parents.append(None)

                item = QStandardItem()
                item.setText(trimmed_line)
                parents[level].appendRow(item)
                parents[level + 1] = item
        QApplication.instance().restoreOverrideCursor()
        return model

    @pyqtSlot(QModelIndex)
    def highlight(self, index):
        proxy = self._completer.completionModel()
        source_index = proxy.mapToSource(index)
        self._tree_view.selectionModel().select(
            source_index,
            QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
        self._tree_view.scrollTo(source_index)

    def about(self):
        QMessageBox.about(
            self, tr("About"),
            tr("This example demonstrates how to use a QCompleter with a custom tree datamodel."
               ))

    def change_case(self, cs):
        self._completer.setCaseSensitivity(
            Qt.CaseSensitive if cs else Qt.CaseInsensitive)

    def update_contents_label(self, sep):
        self._contents_label.setText(
            "Type path from datamodel above with items at each level separated by a '%s'"
            % sep)
예제 #16
0
파일: __init__.py 프로젝트: Zubax/kucher
class RegisterViewWidget(WidgetBase):
    def __init__(self, parent: QWidget):
        super(RegisterViewWidget, self).__init__(parent)

        self._registers = []
        self._running_task: asyncio.Task = None

        self._visibility_selector = QComboBox(self)
        self._visibility_selector.addItem("Show all registers", lambda _: True)
        self._visibility_selector.addItem("Only configuration parameters",
                                          lambda r: r.mutable and r.persistent)

        # noinspection PyUnresolvedReferences
        self._visibility_selector.currentIndexChanged.connect(
            lambda _: self._on_visibility_changed())

        self._reset_selected_button = make_button(
            self,
            "Reset selected",
            icon_name="clear-symbol",
            tool_tip=f"Reset the currently selected registers to their default "
            f"values. The restored values will be committed "
            f"immediately. This function is available only if a "
            f"default value is defined. [{RESET_SELECTED_SHORTCUT}]",
            on_clicked=self._do_reset_selected,
        )

        self._reset_all_button = make_button(
            self,
            "Reset all",
            icon_name="skull-crossbones",
            tool_tip=f"Reset the all registers to their default "
            f"values. The restored values will be committed "
            f"immediately.",
            on_clicked=self._do_reset_all,
        )

        self._read_selected_button = make_button(
            self,
            "Read selected",
            icon_name="process",
            tool_tip=f"Read the currently selected registers only "
            f"[{READ_SELECTED_SHORTCUT}]",
            on_clicked=self._do_read_selected,
        )

        self._read_all_button = make_button(
            self,
            "Read all",
            icon_name="process-plus",
            tool_tip="Read all registers from the device",
            on_clicked=self._do_read_all,
        )

        self._export_button = make_button(
            self,
            "Export",
            icon_name="export",
            tool_tip="Export configuration parameters",
            on_clicked=self._do_export,
        )

        self._import_button = make_button(
            self,
            "Import",
            icon_name="import",
            tool_tip="Import configuration parameters",
            on_clicked=self._do_import,
        )

        self._expand_all_button = make_button(
            self,
            "",
            icon_name="expand-arrow",
            tool_tip="Expand all namespaces",
            on_clicked=lambda: self._tree.expandAll(),
        )

        self._collapse_all_button = make_button(
            self,
            "",
            icon_name="collapse-arrow",
            tool_tip="Collapse all namespaces",
            on_clicked=lambda: self._tree.collapseAll(),
        )

        self._status_display = QLabel(self)
        self._status_display.setWordWrap(True)

        self._reset_selected_button.setEnabled(False)
        self._reset_all_button.setEnabled(False)
        self._read_selected_button.setEnabled(False)
        self._read_all_button.setEnabled(False)
        self._export_button.setEnabled(False)
        self._import_button.setEnabled(False)

        self._tree = QTreeView(self)
        self._tree.setVerticalScrollMode(QTreeView.ScrollPerPixel)
        self._tree.setHorizontalScrollMode(QTreeView.ScrollPerPixel)
        self._tree.setAnimated(True)
        self._tree.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self._tree.setAlternatingRowColors(True)
        self._tree.setContextMenuPolicy(Qt.ActionsContextMenu)

        # Not sure about this one. This hardcoded value may look bad on some platforms.
        self._tree.setIndentation(20)

        def add_action(
            callback: typing.Callable[[], None],
            icon_name: str,
            name: str,
            shortcut: typing.Optional[str] = None,
        ):
            action = QAction(get_icon(icon_name), name, self)
            # noinspection PyUnresolvedReferences
            action.triggered.connect(callback)
            if shortcut:
                action.setShortcut(shortcut)
                action.setAutoRepeat(False)
                try:
                    action.setShortcutVisibleInContextMenu(True)
                except AttributeError:
                    pass  # This feature is not available in PyQt before 5.10

            self._tree.addAction(action)

        add_action(self._do_read_all, "process-plus", "Read all registers")
        add_action(
            self._do_read_selected,
            "process",
            "Read selected registers",
            READ_SELECTED_SHORTCUT,
        )
        add_action(
            self._do_reset_selected,
            "clear-symbol",
            "Reset selected to default",
            RESET_SELECTED_SHORTCUT,
        )

        self._tree.setItemDelegateForColumn(
            int(Model.ColumnIndices.VALUE),
            EditorDelegate(self._tree, self._display_status),
        )

        # It doesn't seem to be explicitly documented, but it seems to be necessary to select either top or bottom
        # decoration position in order to be able to use center alignment. Left or right positions do not work here.
        self._tree.setItemDelegateForColumn(
            int(Model.ColumnIndices.FLAGS),
            StyleOptionModifyingDelegate(
                self._tree,
                decoration_position=QStyleOptionViewItem.Top,  # Important
                decoration_alignment=Qt.AlignCenter,
            ),
        )

        header: QHeaderView = self._tree.header()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)
        header.setStretchLastSection(
            False)  # Horizontal scroll bar doesn't work if this is enabled

        buttons_layout = QGridLayout()
        buttons_layout.addWidget(self._read_selected_button, 0, 0)
        buttons_layout.addWidget(self._reset_selected_button, 0, 2)
        buttons_layout.addWidget(self._read_all_button, 1, 0)
        buttons_layout.addWidget(self._reset_all_button, 1, 2)
        buttons_layout.addWidget(self._import_button, 2, 0)
        buttons_layout.addWidget(self._export_button, 2, 2)

        for col in range(3):
            buttons_layout.setColumnStretch(col, 1)

        layout = lay_out_vertically(
            (self._tree, 1),
            buttons_layout,
            lay_out_horizontally(
                self._visibility_selector,
                (None, 1),
                self._expand_all_button,
                self._collapse_all_button,
            ),
            self._status_display,
        )

        self.setLayout(layout)

    def reset(self):
        self.setup([])

    def setup(self, registers: typing.Iterable[Register]):
        self._registers = list(registers)
        self._on_visibility_changed()

    def _replace_model(
            self, register_visibility_predicate: typing.Callable[[Register],
                                                                 bool]):
        # Cancel all operations that might be pending on the old model
        self._cancel_task()

        old_model = self._tree.model()

        # Configure the new model
        filtered_registers = list(
            filter(register_visibility_predicate, self._registers))
        # It is important to set the Tree widget as the parent in order to let the widget take ownership
        new_model = Model(self._tree, filtered_registers)
        _logger.info("New model %r", new_model)
        self._tree.setModel(new_model)

        # The selection model is implicitly replaced when we replace the model, so it has to be reconfigured
        self._tree.selectionModel().selectionChanged.connect(
            lambda *_: self._on_selection_changed())

        # TODO: Something fishy is going on. Something keeps the old model alive when we're replacing it.
        #       We could call deleteLater() on it, but it seems dangerous, because if that something ever decided
        #       to refer to that dead model later for any reason, we'll get a rougue dangling pointer access on
        #       our hands. The horror!
        if old_model is not None:
            import gc

            model_referrers = gc.get_referrers(old_model)
            if len(model_referrers) > 1:
                _logger.warning(
                    "Extra references to the old model %r: %r",
                    old_model,
                    model_referrers,
                )

        # Update the widget - all root items are expanded by default
        for row in itertools.count():
            index = self._tree.model().index(row, 0)
            if not index.isValid():
                break

            self._tree.expand(index)

        self._reset_selected_button.setEnabled(False)
        self._read_selected_button.setEnabled(False)
        self._read_all_button.setEnabled(len(filtered_registers) > 0)
        self._reset_all_button.setEnabled(len(filtered_registers) > 0)
        self._export_button.setEnabled(len(filtered_registers) > 0)
        self._import_button.setEnabled(len(filtered_registers) > 0)

        self._display_status(f"{len(filtered_registers)} registers loaded")

    def _on_visibility_changed(self):
        self._replace_model(self._visibility_selector.currentData())

    def _on_selection_changed(self):
        selected = self._get_selected_registers()

        self._reset_selected_button.setEnabled(
            any(map(lambda r: r.has_default_value, selected)))
        self._read_selected_button.setEnabled(len(selected) > 0)

    def _do_read_selected(self):
        selected = self._get_selected_registers()
        if selected:
            self._read_specific(selected)
        else:
            self._display_status("No registers are selected, nothing to read")

    def _do_reset_selected(self):
        rv = {}
        for r in self._get_selected_registers():
            if r.has_default_value:
                rv[r] = r.default_value

        self._write_specific(rv)

    def _do_reset_all(self):
        rv = {}
        for r in self._registers:
            if r.has_default_value:
                rv[r] = r.default_value

        self._write_specific(rv)

    def _do_read_all(self):
        self._read_specific(self._tree.model().registers)

    def _do_import(self):
        import_registers(parent=self, registers=self._registers)

    def _do_export(self):
        export_registers(parent=self, registers=self._registers)

    def _read_specific(self, registers: typing.List[Register]):
        total_registers_read = None

        def progress_callback(register: Register, current_register_index: int,
                              total_registers: int):
            nonlocal total_registers_read
            total_registers_read = total_registers
            self._display_status(
                f"Reading {register.name!r} "
                f"({current_register_index + 1} of {total_registers})")

        async def executor():
            try:
                _logger.info("Reading registers: %r",
                             [r.name for r in registers])
                mod: Model = self._tree.model()
                await mod.read(registers=registers,
                               progress_callback=progress_callback)
            except asyncio.CancelledError:
                self._display_status(f"Read has been cancelled")
                raise
            except Exception as ex:
                _logger.exception("Register read failed")
                show_error("Read failed", "Could not read registers", repr(ex),
                           self)
                self._display_status(f"Could not read registers: {ex!r}")
            else:
                self._display_status(
                    f"{total_registers_read} registers have been read")

        self._cancel_task()
        self._running_task = asyncio.get_event_loop().create_task(executor())

    def _write_specific(self, register_value_mapping: typing.Dict[Register,
                                                                  typing.Any]):
        total_registers_assigned = None

        def progress_callback(register: Register, current_register_index: int,
                              total_registers: int):
            nonlocal total_registers_assigned
            total_registers_assigned = total_registers
            self._display_status(
                f"Writing {register.name!r} "
                f"({current_register_index + 1} of {total_registers})")

        async def executor():
            try:
                _logger.info(
                    "Writing registers: %r",
                    [r.name for r in register_value_mapping.keys()],
                )
                mod: Model = self._tree.model()
                await mod.write(
                    register_value_mapping=register_value_mapping,
                    progress_callback=progress_callback,
                )
            except asyncio.CancelledError:
                self._display_status(f"Write has been cancelled")
                raise
            except Exception as ex:
                _logger.exception("Register write failed")
                show_error("Write failed", "Could not read registers",
                           repr(ex), self)
                self._display_status(f"Could not write registers: {ex!r}")
            else:
                self._display_status(
                    f"{total_registers_assigned} registers have been written")

        self._cancel_task()
        self._running_task = asyncio.get_event_loop().create_task(executor())

    def _get_selected_registers(self) -> typing.List[Register]:
        selected_indexes: typing.List[
            QModelIndex] = self._tree.selectedIndexes()
        selected_registers = set()
        for si in selected_indexes:
            r = Model.get_register_from_index(si)
            if r is not None:
                selected_registers.add(r)
        # Beware that sets are not sorted, this may lead to weird user experience when watching the registers
        # read in a funny order.
        return list(sorted(selected_registers, key=lambda x: x.name))

    def _cancel_task(self):
        # noinspection PyBroadException
        try:
            self._running_task.cancel()
        except Exception:
            pass
        else:
            _logger.info("A running task had to be cancelled: %r",
                         self._running_task)
        finally:
            self._running_task = None

    def _display_status(self, text=None):
        self._status_display.setText(text)
예제 #17
0
class FileTreeView(QWidget):
    on_menu_select = pyqtSignal(str, dict)

    def __init__(self):
        super().__init__()
        self.list_file = []
        self.get_data_file()

        v_box = QVBoxLayout()
        v_box.addLayout(self.finder())
        v_box.addWidget(self.tree_view())
        self.setLayout(v_box)

    def finder(self):
        w_find = QLineEdit()
        w_find.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))

        w_find_button = QPushButton()
        w_find_button.setText("Find")
        w_find_button.setFixedWidth(50)

        h_box = QHBoxLayout()
        h_box.addWidget(w_find)
        h_box.addWidget(w_find_button, 1)
        return h_box

    def get_data_file(self):
        self.list_file = self.create_list(get_data_folder())

    def create_list(self, dir):
        lst = os.listdir(path=dir)
        list_file = []
        for f in lst:
            path = dir + "\\" + f
            file = {}
            if os.path.isdir(path):
                file["dir"] = dir
                file["name"] = f
                file["isDir"] = True
                file["child"] = self.create_list(path)
            else:
                file["dir"] = dir
                file["name"] = f
                file["isDir"] = False

            list_file.append(file)
        return list_file

    def tree_view(self):
        self.tree = QTreeView()
        self.tree.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))

        self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree.customContextMenuRequested.connect(self.openMenu)

        self.model = QtGui.QStandardItemModel()
        self.model.setHorizontalHeaderLabels(['List API'])
        self.tree.header().setDefaultSectionSize(180)
        self.tree.setModel(self.model)
        self.import_data(self.model.invisibleRootItem(), self.list_file)
        self.tree.expandAll()

        return self.tree

    def import_data(self, parent_item, list):
        for i in list:
            if i["isDir"]:
                item = QStandardItem(i["name"])
                item.setEditable(False)
                item.setIcon(QIcon(get_icon_link("folder_yellow.svg")))
                item.setData(i)
                parent_item.appendRow(item)
                self.import_data(item, i["child"])
            else:
                item = QStandardItem(i["name"])
                item.setEditable(False)
                item.setIcon(QIcon(get_icon_link("text_snippet.svg")))
                item.setData(i)
                parent_item.appendRow(item)

    def openMenu(self, position):
        indexes = self.tree.selectedIndexes()
        level = 0
        data = {}
        if len(indexes) > 0:
            index = indexes[0]
            item = self.model.itemFromIndex(index)
            data = item.data()
            if data['isDir']:
                level = 1
            else:
                level = 2

        menu = QMenu()
        menu.setStyleSheet(open('stylesheet/default.qss').read())

        # Create preference action
        rename_action = QAction(QIcon(get_icon_link('edit.svg')), '&Rename',
                                self)
        rename_action.setStatusTip('Rename')
        # exitAction.triggered.connect(self.exitCall)

        # Create preference action
        new_action = QAction(QIcon(get_icon_link('create_new_folder.svg')),
                             '&New Folder', self)
        new_action.setStatusTip('New Folder')
        # exitAction.triggered.connect(self.exitCall)

        # Create preference action
        refresh_action = QAction(QIcon(get_icon_link('refresh.svg')),
                                 '&Refresh', self)
        refresh_action.setStatusTip('Refresh')
        # exitAction.triggered.connect(self.exitCall)

        # Create preference action
        delete_action = QAction(QIcon(get_icon_link('delete_forever.svg')),
                                '&Delete', self)
        delete_action.setStatusTip('Delete')
        # exitAction.triggered.connect(self.exitCall)

        # Create preference action
        open_action = QAction(QIcon(get_icon_link('open_in_new.svg')), '&Open',
                              self)
        open_action.setStatusTip('Open file')
        # open_action.triggered.connect(self.open_file)

        # Create preference action
        expand_action = QAction(QIcon(), '&Expand', self)
        expand_action.setStatusTip('Expand')
        # exitAction.triggered.connect(self.exitCall)

        # Create preference action
        collapse_action = QAction(QIcon(), '&Collapse', self)
        collapse_action.setStatusTip('Collapse')
        # exitAction.triggered.connect(self.exitCall)

        # Create preference action
        copy_action = QAction(QIcon(get_icon_link('content_copy.svg')),
                              '&Copy', self)
        copy_action.setStatusTip('Copy')
        # exitAction.triggered.connect(self.exitCall)

        # Create preference action
        move_action = QAction(QIcon(get_icon_link('zoom_out_map.svg')),
                              '&Move', self)
        move_action.setStatusTip('Move')
        # exitAction.triggered.connect(self.exitCall)

        if level == 1:
            menu.addAction(rename_action)
            menu.addAction(new_action)
            menu.addSeparator()
            menu.addAction(refresh_action)
            menu.addAction(expand_action)
            menu.addAction(collapse_action)
            menu.addSeparator()
            menu.addAction(delete_action)
        elif level == 2:
            menu.addAction(open_action)
            menu.addAction(refresh_action)
            menu.addSeparator()
            menu.addAction(rename_action)
            menu.addAction(copy_action)
            menu.addAction(move_action)
            menu.addSeparator()
            menu.addAction(delete_action)
        else:
            menu.addAction(new_action)
            menu.addAction(refresh_action)

        action = menu.exec_(self.tree.viewport().mapToGlobal(position))
        if action == open_action:
            self.on_menu_select.emit("open", data)
예제 #18
0
class FileTreeView(QWidget):
    on_menu_select = pyqtSignal(str, str)
    on_dir_change = pyqtSignal()

    def __init__(self, parent: Application):
        super().__init__()
        self.stopped = threading.Event()
        self.tree = QTreeView()
        self.handle = self.Handler(self)
        self.get_data_file()
        self.model = QtGui.QStandardItemModel()
        self.item_construct = {}

        v_box = QVBoxLayout()
        v_box.addWidget(self.finder())
        v_box.addWidget(self.tree_view())
        self.setLayout(v_box)
        self.watch_dog()
        parent.app_close.connect(self.exit_push)

        self.tree.setAlternatingRowColors(True)

    def exit_push(self):
        self.stopped.set()

    def finder(self):
        w_find = QLineEdit()
        w_find.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
        w_find.textChanged.connect(self.sort_list)
        w_find.setPlaceholderText("Search file..")

        return w_find

    def sort_list(self, text):
        list_sort = []
        if text == "":
            self.show_tree(self.list_file)
        else:
            for data in self.list_file:
                if text.lower() in data.name().lower():
                    list_sort.append(data)
            self.show_tree(list_sort)

    def watch_dog(self):
        watch = ProcessRunnable(target=watch_winform,
                                args=(get_data_folder(), self.handle,
                                      self.stopped))
        watch.start()

    class Handler(watchdog.events.PatternMatchingEventHandler):
        def __init__(self, parent):
            super().__init__()
            self.parent = parent

        def on_created(self, event):
            print("Watchdog received created event", event.src_path, sep=" : ")
            asyncio.run(
                self.parent.file_change('create', event.src_path,
                                        event.is_directory))

        def on_modified(self, event):
            print("Watchdog received modified event",
                  event.src_path,
                  sep=" : ")
            asyncio.run(
                self.parent.file_change('modify', event.src_path,
                                        event.is_directory))

        def on_moved(self, event):
            print("Watchdog received move event",
                  event.src_path,
                  event.dest_path,
                  sep=" : ")
            asyncio.run(
                self.parent.file_change('move', event.src_path,
                                        event.is_directory, event.dest_path))

        def on_deleted(self, event):
            print("Watchdog received delete event", event.src_path, sep=" : ")
            asyncio.run(
                self.parent.file_change('delete', event.src_path,
                                        event.is_directory))

    async def file_change(self, tpe, old, is_directory, new=""):
        if tpe == "move":
            self.import_single(self.model.invisibleRootItem(), new)
            self.remove_single(old)
        elif tpe == "delete":
            self.remove_single(old)
        elif tpe == "create":
            self.import_single(self.model.invisibleRootItem(), old)

        if is_directory:
            self.on_dir_change.emit()

    def get_data_file(self):
        self.list_file = []
        self.create_list(get_data_folder())

    def create_list(self, dir):
        lst = os.listdir(path=dir)
        for f in lst:
            path = os.path.join(dir, f)
            file = MyFile()
            if os.path.isdir(path):
                file.setParentName(os.path.basename(dir))
                file.setParent(dir)
                file.setName(f)
                file.setDir(True)
                self.list_file.append(file)
                self.create_list(path)
            else:
                file.setParentName(os.path.basename(dir))
                file.setParent(dir)
                file.setName(f)
                file.setDir(False)
                self.list_file.append(file)

    def tree_view(self):
        self.tree.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))

        self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree.customContextMenuRequested.connect(self.open_menu)
        self.tree.doubleClicked.connect(self.open_event)
        # self.model.itemChanged.connect(self.data_change)
        self.tree.setModel(self.model)
        self.show_tree(self.list_file)
        return self.tree

    def show_tree(self, list_file):
        self.model.clear()
        self.model.setHorizontalHeaderLabels(['List API'])
        self.tree.header().setDefaultSectionSize(180)
        parent = self.model.invisibleRootItem()
        self.item_construct = {}
        self.import_data(parent, list_file)
        self.tree.expandAll()

    def import_data_by_path(self, parent, path: list, index):
        if index < len(path):
            full = os.sep.join(path[:index + 1])
            if full in self.item_construct:
                item = self.item_construct[full]
            else:
                item = QStandardItem(path[index])
                if os.path.isfile(os.path.join(get_data_folder(), full)):
                    item.setToolTip(
                        self.read_description(
                            os.path.join(get_data_folder(), full)))
                item.setEditable(False)
                if not os.path.isdir(os.path.join(get_data_folder(), full)):
                    item.setIcon(QIcon(get_icon_link("text_snippet.svg")))
                else:
                    item.setIcon(QIcon(get_icon_link("folder_yellow.svg")))

                item.setData(full)
                parent.appendRow(item)
                self.item_construct[full] = item

            self.import_data_by_path(item, path, index + 1)

    def read_description(self, path):
        try:
            data = json.loads(open(path, encoding='utf-8').read())
            json_data = APIData()
            json_data.construct(data)
            x = json_data.parseSave().description()
            if x.isspace() or x == "":
                return ".."
            else:
                return x
        except Exception as ex:
            print(ex)
            return ".."

    def import_data(self, parent, list_data):
        for i in list_data:
            self.import_single(parent, i)

    def import_single(self, parent, file_path):
        path = self.path_extract(file_path)
        self.import_data_by_path(parent, path, 0)

    def remove_single(self, file_path):
        path = self.path_extract(file_path)
        full = os.sep.join(path[:len(path)])
        if full in self.item_construct:
            item = self.item_construct[full]
            (item.parent()
             or self.model.invisibleRootItem()).removeRow(item.row())
            del self.item_construct[full]

    def path_extract(self, file_path):
        if isinstance(file_path, MyFile):
            path = os.path.join(file_path.parent(), file_path.name())
        else:
            path = file_path

        path = path.replace(get_data_folder(), "")
        if path.startswith(os.sep):
            path = path.replace(os.sep, "", 1)
        path = path.split(os.sep)
        return path

    def open_menu(self, position):
        indexes = self.tree.selectedIndexes()
        level = 0
        data = ""
        item = None
        if len(indexes) > 0:
            index = indexes[0]
            item = self.model.itemFromIndex(index)
            data = item.data()
            data = os.path.join(get_data_folder(), data)
            if os.path.isdir(data):
                level = 1
            else:
                level = 2

        menu = QMenu()
        menu.setStyleSheet(open(get_stylesheet()).read())

        rename_action = QAction(QIcon(get_icon_link('edit.svg')), '&Rename',
                                self)
        rename_action.setStatusTip('Rename')

        new_action = QAction(QIcon(get_icon_link('create_new_folder.svg')),
                             '&New Folder', self)
        new_action.setStatusTip('New Folder')

        refresh_action = QAction(QIcon(get_icon_link('refresh.svg')),
                                 '&Refresh', self)
        refresh_action.setStatusTip('Refresh')

        delete_action = QAction(QIcon(get_icon_link('delete_forever.svg')),
                                '&Delete', self)
        delete_action.setStatusTip('Delete')

        open_action = QAction(QIcon(get_icon_link('open_in_new.svg')), '&Open',
                              self)
        open_action.setStatusTip('Open file')

        expand_action = QAction(QIcon(), '&Expand', self)
        expand_action.setStatusTip('Expand')

        collapse_action = QAction(QIcon(), '&Collapse', self)
        collapse_action.setStatusTip('Collapse')

        duplicate_action = QAction(QIcon(get_icon_link('content_copy.svg')),
                                   '&Duplicate', self)
        duplicate_action.setStatusTip('Duplicate')

        copy_action = QAction(QIcon(get_icon_link('content_copy.svg')),
                              '&Copy', self)
        copy_action.setStatusTip('Copy')

        move_action = QAction(QIcon(get_icon_link('zoom_out_map.svg')),
                              '&Move', self)
        move_action.setStatusTip('Move')

        if level == 1:
            menu.addAction(rename_action)
            menu.addAction(new_action)
            menu.addSeparator()
            menu.addAction(refresh_action)
            menu.addAction(expand_action)
            menu.addAction(collapse_action)
            menu.addSeparator()
            menu.addAction(delete_action)
        elif level == 2:
            menu.addAction(open_action)
            menu.addAction(new_action)
            menu.addAction(refresh_action)
            menu.addSeparator()
            menu.addAction(rename_action)
            menu.addAction(duplicate_action)
            menu.addAction(copy_action)
            menu.addAction(move_action)
            menu.addSeparator()
            menu.addAction(delete_action)
        else:
            menu.addAction(new_action)
            menu.addAction(refresh_action)

        action = menu.exec_(self.tree.viewport().mapToGlobal(position))

        if action == open_action:
            if data != "":
                self.on_menu_select.emit("open", data)
        elif action == refresh_action:
            self.get_data_file()
            self.show_tree(self.list_file)
        elif action == expand_action:
            if item is not None:
                self.tree.expand(item.index())
        elif action == collapse_action:
            if item is not None:
                self.tree.collapse(item.index())
        elif action == delete_action:
            if data != "":
                msg = QMessageBox()
                msg.setStyleSheet(open(get_stylesheet()).read())
                msg.setIcon(QMessageBox.Warning)
                msg.setBaseSize(QSize(500, 300))
                msg.setText("Delete file.")
                msg.setInformativeText("Are you sure to detele " +
                                       os.path.basename(data) + "?")
                msg.setWindowTitle("Delete Warning!!!")
                msg.addButton('Delete', QMessageBox.YesRole)
                msg.addButton('Move to Trash', QMessageBox.YesRole)
                msg.addButton('Cancel', QMessageBox.NoRole)

                rs = msg.exec_()
                if rs == 0:
                    if os.path.isdir(data):
                        shutil.rmtree(data)
                    else:
                        os.remove(data)
                elif rs == 1:
                    send2trash(data)
        elif action == new_action:
            if data == "":
                data = get_data_folder()
            # input_name = QInputDialog()
            # input_name.setStyleSheet(open(get_stylesheet()).read())
            # text, ok = input_name.getText(self, 'New Folder', 'Folder name:')
            inp = QComboDialog('New Folder', 'Folder name:', QComboDialog.Text)
            ok = inp.exec_()
            if ok and inp.select:
                if os.path.isdir(data):
                    try:
                        os.mkdir(os.path.join(data, inp.select))
                    except Exception as ex:
                        alert = Alert("Error", "Create folder error", str(ex))
                        alert.exec_()
                        print(ex)
                else:
                    new = os.path.join(os.path.dirname(data), inp.select)
                    try:
                        os.mkdir(new)
                    except Exception as ex:
                        alert = Alert("Error", "Create folder error", str(ex))
                        alert.exec_()
                        print(ex)
        elif action == rename_action:
            if data != "":
                # input_name = QInputDialog()
                # input_name.setStyleSheet(open(get_stylesheet()).read())
                # text, ok = input_name.getText(self, 'Rename file', 'New name:')
                inp = QComboDialog('Rename file', 'New name:',
                                   QComboDialog.Text)
                ok = inp.exec_()
                if ok and inp.select:
                    if os.path.isdir(data):
                        new = os.path.join(os.path.dirname(data), inp.select)
                        try:
                            os.rename(data, new)
                        except Exception as ex:
                            alert = Alert("Error", "Rename folder error",
                                          str(ex))
                            alert.exec_()
                            print(ex)
                    else:
                        filename, file_extension = os.path.splitext(data)
                        new = os.path.join(os.path.dirname(data),
                                           inp.select + file_extension)
                        try:
                            os.rename(data, new)
                        except Exception as ex:
                            alert = Alert("Error", "Rename file error",
                                          str(ex))
                            alert.exec_()
                            print(ex)
        elif action == move_action:
            if data != "":
                items = get_list_folder(get_data_folder(), get_data_folder())
                #
                # item, ok = QInputDialog.getItem(self, "Select folder dialog",
                #                                 "Select the destination folder", items, 0, False)

                inp = QComboDialog("Move", "Select the destination folder",
                                   QComboDialog.ComboBox, items)
                ok = inp.exec_()

                if ok and inp.select:
                    folder = inp.select
                    if inp.select.startswith(os.sep):
                        folder = inp.select.replace(os.sep, "", 1)
                    new = os.path.join(get_data_folder(), folder,
                                       os.path.basename(data))
                    try:
                        os.rename(data, new)
                    except Exception as ex:
                        alert = Alert("Error", "Move file error", str(ex))
                        alert.exec_()
                        print(ex)
        elif action == duplicate_action:
            if data != "":
                # input_name = QInputDialog()
                # input_name.setStyleSheet(open(get_stylesheet()).read())
                # text, ok = input_name.getText(self, 'Duplicate file', 'New name:')
                inp = QComboDialog('Duplicate file', 'New name:',
                                   QComboDialog.Text)
                filename, file_extension = os.path.splitext(data)
                inp.set_init_text(filename)
                ok = inp.exec_()
                if ok and inp.select:
                    new = os.path.join(os.path.dirname(data),
                                       inp.select + file_extension)
                    try:
                        copyfile(data, new)
                    except Exception as ex:
                        alert = Alert("Error", "Duplicate file error", str(ex))
                        alert.exec_()
                        print(ex)
        elif action == copy_action:
            if data != "":
                items = get_list_folder(get_data_folder(), get_data_folder())
                inp = QComboDialog("Copy", "Select the destination folder",
                                   QComboDialog.ComboBox, items)
                ok = inp.exec_()
                # item, ok = QInputDialog.getItem(self, "Select folder dialog",
                #                                 "Select the destination folder", items, 0, False)

                if ok and inp.select:
                    folder = inp.select
                    if inp.select.startswith(os.sep):
                        folder = inp.select.replace(os.sep, "", 1)
                    new = os.path.join(get_data_folder(), folder,
                                       os.path.basename(data))
                    try:
                        copyfile(data, new)
                    except Exception as ex:
                        alert = Alert("Error", "Copy file error", str(ex))
                        alert.exec_()
                        print(ex)

    def open_event(self, index):
        item = self.model.itemFromIndex(index)
        if item is not None:
            data = item.data()
            data = os.path.join(get_data_folder(), data)
            if os.path.isdir(data):
                level = 1
            else:
                level = 2
            if data != "":
                if level == 2:
                    self.on_menu_select.emit("open", data)
                elif level == 1:
                    if not self.tree.isExpanded(item.index()):
                        self.tree.collapse(item.index())
                    else:
                        self.tree.expand(item.index())
예제 #19
0
class DirectoriesDialog(QMainWindow):
    def __init__(self, app, **kwargs):
        super().__init__(None, **kwargs)
        self.app = app
        self.specific_actions = set()
        self.lastAddedFolder = platform.INITIAL_FOLDER_IN_DIALOGS
        self.recentFolders = Recent(self.app, "recentFolders")
        self._setupUi()
        self._updateScanTypeList()
        self.directoriesModel = DirectoriesModel(self.app.model.directory_tree,
                                                 view=self.treeView)
        self.directoriesDelegate = DirectoriesDelegate()
        self.treeView.setItemDelegate(self.directoriesDelegate)
        self._setupColumns()
        self.app.recentResults.addMenu(self.menuLoadRecent)
        self.app.recentResults.addMenu(self.menuRecentResults)
        self.recentFolders.addMenu(self.menuRecentFolders)
        self._updateAddButton()
        self._updateRemoveButton()
        self._updateLoadResultsButton()
        self._updateActionsState()
        self._setupBindings()

    def _setupBindings(self):
        self.appModeRadioBox.itemSelected.connect(self.appModeButtonSelected)
        self.showPreferencesButton.clicked.connect(
            self.app.actionPreferences.trigger)
        self.scanButton.clicked.connect(self.scanButtonClicked)
        self.loadResultsButton.clicked.connect(self.actionLoadResults.trigger)
        self.addFolderButton.clicked.connect(self.actionAddFolder.trigger)
        self.removeFolderButton.clicked.connect(self.removeFolderButtonClicked)
        self.treeView.selectionModel().selectionChanged.connect(
            self.selectionChanged)
        self.app.recentResults.itemsChanged.connect(
            self._updateLoadResultsButton)
        self.recentFolders.itemsChanged.connect(self._updateAddButton)
        self.recentFolders.mustOpenItem.connect(self.app.model.add_directory)
        self.directoriesModel.foldersAdded.connect(
            self.directoriesModelAddedFolders)
        self.app.willSavePrefs.connect(self.appWillSavePrefs)

    def _setupActions(self):
        # (name, shortcut, icon, desc, func)
        ACTIONS = [
            (
                "actionLoadResults",
                "Ctrl+L",
                "",
                tr("Load Results..."),
                self.loadResultsTriggered,
            ),
            (
                "actionShowResultsWindow",
                "",
                "",
                tr("Scan Results"),
                self.app.showResultsWindow,
            ),
            ("actionAddFolder", "", "", tr("Add Folder..."),
             self.addFolderTriggered),
            ("actionLoadDirectories", "", "", tr("Load Directories..."),
             self.loadDirectoriesTriggered),
            ("actionSaveDirectories", "", "", tr("Save Directories..."),
             self.saveDirectoriesTriggered),
        ]
        createActions(ACTIONS, self)
        if self.app.use_tabs:
            # Keep track of actions which should only be accessible from this window
            self.specific_actions.add(self.actionLoadDirectories)
            self.specific_actions.add(self.actionSaveDirectories)

    def _setupMenu(self):
        if not self.app.use_tabs:
            # we are our own QMainWindow, we need our own menu bar
            self.menubar = QMenuBar(self)
            self.menubar.setGeometry(QRect(0, 0, 42, 22))
            self.menuFile = QMenu(self.menubar)
            self.menuFile.setTitle(tr("File"))
            self.menuView = QMenu(self.menubar)
            self.menuView.setTitle(tr("View"))
            self.menuHelp = QMenu(self.menubar)
            self.menuHelp.setTitle(tr("Help"))
            self.setMenuBar(self.menubar)
            menubar = self.menubar
        else:
            # we are part of a tab widget, we populate its window's menubar instead
            self.menuFile = self.app.main_window.menuFile
            self.menuView = self.app.main_window.menuView
            self.menuHelp = self.app.main_window.menuHelp
            menubar = self.app.main_window.menubar

        self.menuLoadRecent = QMenu(self.menuFile)
        self.menuLoadRecent.setTitle(tr("Load Recent Results"))

        self.menuFile.addAction(self.actionLoadResults)
        self.menuFile.addAction(self.menuLoadRecent.menuAction())
        self.menuFile.addSeparator()
        self.menuFile.addAction(self.app.actionClearPictureCache)
        self.menuFile.addSeparator()
        self.menuFile.addAction(self.actionLoadDirectories)
        self.menuFile.addAction(self.actionSaveDirectories)
        self.menuFile.addSeparator()
        self.menuFile.addAction(self.app.actionQuit)

        self.menuView.addAction(self.app.actionDirectoriesWindow)
        self.menuView.addAction(self.actionShowResultsWindow)
        self.menuView.addAction(self.app.actionIgnoreList)
        self.menuView.addSeparator()
        self.menuView.addAction(self.app.actionPreferences)

        self.menuHelp.addAction(self.app.actionShowHelp)
        self.menuHelp.addAction(self.app.actionOpenDebugLog)
        self.menuHelp.addAction(self.app.actionAbout)

        menubar.addAction(self.menuFile.menuAction())
        menubar.addAction(self.menuView.menuAction())
        menubar.addAction(self.menuHelp.menuAction())

        # Recent folders menu
        self.menuRecentFolders = QMenu()
        self.menuRecentFolders.addAction(self.actionAddFolder)
        self.menuRecentFolders.addSeparator()

        # Recent results menu
        self.menuRecentResults = QMenu()
        self.menuRecentResults.addAction(self.actionLoadResults)
        self.menuRecentResults.addSeparator()

    def _setupUi(self):
        self.setWindowTitle(self.app.NAME)
        self.resize(420, 338)
        self.centralwidget = QWidget(self)
        self.verticalLayout = QVBoxLayout(self.centralwidget)
        self.verticalLayout.setContentsMargins(4, 0, 4, 0)
        self.verticalLayout.setSpacing(0)
        hl = QHBoxLayout()
        label = QLabel(tr("Application Mode:"), self)
        label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        hl.addWidget(label)
        self.appModeRadioBox = RadioBox(
            self,
            items=[tr("Standard"), tr("Music"),
                   tr("Picture")],
            spread=False)
        hl.addWidget(self.appModeRadioBox)
        self.verticalLayout.addLayout(hl)
        hl = QHBoxLayout()
        hl.setAlignment(Qt.AlignLeft)
        label = QLabel(tr("Scan Type:"), self)
        label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        hl.addWidget(label)
        self.scanTypeComboBox = QComboBox(self)
        self.scanTypeComboBox.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
        self.scanTypeComboBox.setMaximumWidth(400)
        hl.addWidget(self.scanTypeComboBox)
        self.showPreferencesButton = QPushButton(tr("More Options"),
                                                 self.centralwidget)
        self.showPreferencesButton.setSizePolicy(QSizePolicy.Fixed,
                                                 QSizePolicy.Fixed)
        hl.addWidget(self.showPreferencesButton)
        self.verticalLayout.addLayout(hl)
        self.promptLabel = QLabel(
            tr('Select folders to scan and press "Scan".'), self.centralwidget)
        self.verticalLayout.addWidget(self.promptLabel)
        self.treeView = QTreeView(self.centralwidget)
        self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.treeView.setAcceptDrops(True)
        triggers = (QAbstractItemView.DoubleClicked
                    | QAbstractItemView.EditKeyPressed
                    | QAbstractItemView.SelectedClicked)
        self.treeView.setEditTriggers(triggers)
        self.treeView.setDragDropOverwriteMode(True)
        self.treeView.setDragDropMode(QAbstractItemView.DropOnly)
        self.treeView.setUniformRowHeights(True)
        self.verticalLayout.addWidget(self.treeView)
        self.horizontalLayout = QHBoxLayout()
        self.removeFolderButton = QPushButton(self.centralwidget)
        self.removeFolderButton.setIcon(QIcon(QPixmap(":/minus")))
        self.removeFolderButton.setShortcut("Del")
        self.horizontalLayout.addWidget(self.removeFolderButton)
        self.addFolderButton = QPushButton(self.centralwidget)
        self.addFolderButton.setIcon(QIcon(QPixmap(":/plus")))
        self.horizontalLayout.addWidget(self.addFolderButton)
        spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding,
                                  QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem1)
        self.loadResultsButton = QPushButton(self.centralwidget)
        self.loadResultsButton.setText(tr("Load Results"))
        self.horizontalLayout.addWidget(self.loadResultsButton)
        self.scanButton = QPushButton(self.centralwidget)
        self.scanButton.setText(tr("Scan"))
        self.scanButton.setDefault(True)
        self.horizontalLayout.addWidget(self.scanButton)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.setCentralWidget(self.centralwidget)

        self._setupActions()
        self._setupMenu()

        if self.app.prefs.directoriesWindowRect is not None:
            self.setGeometry(self.app.prefs.directoriesWindowRect)
        else:
            moveToScreenCenter(self)

    def _setupColumns(self):
        header = self.treeView.header()
        header.setStretchLastSection(False)
        header.setSectionResizeMode(0, QHeaderView.Stretch)
        header.setSectionResizeMode(1, QHeaderView.Fixed)
        header.resizeSection(1, 100)

    def _updateActionsState(self):
        self.actionShowResultsWindow.setEnabled(
            self.app.resultWindow is not None)

    def _updateAddButton(self):
        if self.recentFolders.isEmpty():
            self.addFolderButton.setMenu(None)
        else:
            self.addFolderButton.setMenu(self.menuRecentFolders)

    def _updateRemoveButton(self):
        indexes = self.treeView.selectedIndexes()
        if not indexes:
            self.removeFolderButton.setEnabled(False)
            return
        self.removeFolderButton.setEnabled(True)

    def _updateLoadResultsButton(self):
        if self.app.recentResults.isEmpty():
            self.loadResultsButton.setMenu(None)
        else:
            self.loadResultsButton.setMenu(self.menuRecentResults)

    def _updateScanTypeList(self):
        try:
            self.scanTypeComboBox.currentIndexChanged[int].disconnect(
                self.scanTypeChanged)
        except TypeError:
            # Not connected, ignore
            pass
        self.scanTypeComboBox.clear()
        scan_options = self.app.model.SCANNER_CLASS.get_scan_options()
        for scan_option in scan_options:
            self.scanTypeComboBox.addItem(scan_option.label)
        SCAN_TYPE_ORDER = [so.scan_type for so in scan_options]
        selected_scan_type = self.app.prefs.get_scan_type(
            self.app.model.app_mode)
        scan_type_index = SCAN_TYPE_ORDER.index(selected_scan_type)
        self.scanTypeComboBox.setCurrentIndex(scan_type_index)
        self.scanTypeComboBox.currentIndexChanged[int].connect(
            self.scanTypeChanged)
        self.app._update_options()

    # --- QWidget overrides
    def closeEvent(self, event):
        event.accept()
        if self.app.model.results.is_modified:
            title = tr("Unsaved results")
            msg = tr("You have unsaved results, do you really want to quit?")
            if not self.app.confirm(title, msg):
                event.ignore()
        if event.isAccepted():
            self.app.shutdown()

    # --- Events
    def addFolderTriggered(self):
        title = tr("Select a folder to add to the scanning list")
        flags = QFileDialog.ShowDirsOnly
        dirpath = str(
            QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder,
                                             flags))
        if not dirpath:
            return
        self.lastAddedFolder = dirpath
        self.app.model.add_directory(dirpath)
        self.recentFolders.insertItem(dirpath)

    def appModeButtonSelected(self, index):
        if index == 2:
            mode = AppMode.Picture
        elif index == 1:
            mode = AppMode.Music
        else:
            mode = AppMode.Standard
        self.app.model.app_mode = mode
        self._updateScanTypeList()

    def appWillSavePrefs(self):
        self.app.prefs.directoriesWindowRect = self.geometry()

    def directoriesModelAddedFolders(self, folders):
        for folder in folders:
            self.recentFolders.insertItem(folder)

    def loadResultsTriggered(self):
        title = tr("Select a results file to load")
        files = ";;".join(
            [tr("dupeGuru Results (*.dupeguru)"),
             tr("All Files (*.*)")])
        destination = QFileDialog.getOpenFileName(self, title, "", files)[0]
        if destination:
            self.app.model.load_from(destination)
            self.app.recentResults.insertItem(destination)

    def loadDirectoriesTriggered(self):
        title = tr("Select a directories file to load")
        files = ";;".join(
            [tr("dupeGuru Results (*.dupegurudirs)"),
             tr("All Files (*.*)")])
        destination = QFileDialog.getOpenFileName(self, title, "", files)[0]
        if destination:
            self.app.model.load_directories(destination)

    def removeFolderButtonClicked(self):
        self.directoriesModel.model.remove_selected()

    def saveDirectoriesTriggered(self):
        title = tr("Select a file to save your directories to")
        files = tr("dupeGuru Directories (*.dupegurudirs)")
        destination, chosen_filter = QFileDialog.getSaveFileName(
            self, title, "", files)
        if destination:
            if not destination.endswith(".dupegurudirs"):
                destination = "{}.dupegurudirs".format(destination)
            self.app.model.save_directories_as(destination)

    def scanButtonClicked(self):
        if self.app.model.results.is_modified:
            title = tr("Start a new scan")
            msg = tr(
                "You have unsaved results, do you really want to continue?")
            if not self.app.confirm(title, msg):
                return
        self.app.model.start_scanning()

    def scanTypeChanged(self, index):
        scan_options = self.app.model.SCANNER_CLASS.get_scan_options()
        self.app.prefs.set_scan_type(self.app.model.app_mode,
                                     scan_options[index].scan_type)
        self.app._update_options()

    def selectionChanged(self, selected, deselected):
        self._updateRemoveButton()
예제 #20
0
class EditScreen(QWidget):
    def __init__(self, rester: Rester):
        super(EditScreen, self).__init__()
        self.last_item_type = ''

        self.rester = rester
        self.tree = QTreeView()
        layout = QVBoxLayout()
        layout.addWidget(self.tree)
        self.model = QStandardItemModel()
        self.model.setHorizontalHeaderLabels(['Name'])
        self.tree.header().setDefaultSectionSize(200)
        self.tree.setModel(self.model)
        self.data = self.rester.list_request(list(APIEnum))
        self.import_data(self.data)
        self.tree.setSortingEnabled(False)
        self.tree.expandAll()
        self.tree.setSelectionMode(QAbstractItemView.SingleSelection)
        self.tree.selectionModel().selectionChanged.connect(self.item_selected)

        self.init_ui()

    def init_ui(self):
        hbox = QHBoxLayout()

        vbox = QVBoxLayout()
        left_layout = QFormLayout()
        tree_group_box = QGroupBox()
        self.search_field = QLineEdit()
        # self.search_field.editingFinished.connect(self.search_field_changed)
        self.search_field.textEdited.connect(self.search_field_changed)
        left_layout.addRow(QLabel('Search:'), self.search_field)
        tree_group_box.setLayout(left_layout)

        vbox.addWidget(tree_group_box)
        vbox.addWidget(self.tree)

        gb = QGroupBox()
        gb.setLayout(vbox)

        self.splitter = QSplitter()
        self.splitter.addWidget(gb)

        self.ew = EquipmentEdit()
        self.splitter.addWidget(self.ew)

        hbox.addWidget(self.splitter)

        self.setLayout(hbox)

    def import_data(self, data, root=None):

        self.model.setRowCount(0)
        if root is None:
            root = self.model.invisibleRootItem()
        for key, value_list in data.items():
            parent = TreeItem({'name': key}, '', parent=True)
            parent.setEditable(False)
            root.appendRow([parent])
            for value in value_list:
                parent.appendRow([TreeItem(value, key)])

    def search_field_changed(self):
        term = self.search_field.text()
        data = {}
        for key, value_list in self.data.items():
            li = []
            for value in value_list:
                for k in value.keys():
                    if term in k:
                        li.append(value)
            data[key] = li

        self.import_data(data)

    def item_selected(self):
        indexes = self.tree.selectedIndexes()
        selected = indexes[0]

        item = self.model.itemFromIndex(selected)

        if item.is_parent:
            return

        #if item.typ != self.last_item_type:
        self.ew.hide()
        self.ew.destroy()
        self.ew = get_edit_widget(item.typ, self.data)
        self.splitter.addWidget(self.ew)

        self.last_item_type = item.typ

        data_dict = item.get_data_dict()
        self.ew.load_data(data_dict)
예제 #21
0
class ZeroConfExplorer(QWidget):
    """
    create a zeroconf qgroubbox with a qlist view 
    """
    def __init__(self, name):
        super(ZeroConfExplorer, self).__init__()
        # init explorer to None
        self.oscquery_device = None
        if not name:
            name = 'OSCJSON thru TCP Explorer'
        # create the view
        self.explorer = QTreeView()
        # Hide Useless Header
        self.explorer.header().hide()
        self.panel = Panel()
        # create right-click menu
        self.explorer.setContextMenuPolicy(Qt.CustomContextMenu)
        self.explorer.customContextMenuRequested.connect(self.contextual_menu)
        # create the model
        self.devices_model = QStandardItemModel()
        # link model to the view
        self.explorer.setModel(self.devices_model)
        self.explorer.selectionModel().selectionChanged.connect(
            self.selection_updated)
        # set selection
        self.device_selection_model = self.explorer.selectionModel()
        # set layout and group
        Layout = QGridLayout()
        # add the view to the layout
        Layout.addWidget(self.explorer, 0, 0)
        Layout.addWidget(self.panel, 0, 1)
        # add the layout to the GroupBox
        self.setLayout(Layout)
        #self.setMinimumSize(300, 300)
        self.explorer.setFixedSize(300, 300)
        # start zeroconf services
        zeroconf = Zeroconf()
        # start the callback, it will create items
        listener = ZeroConfListener(self.devices_model)
        listener.add_device.connect(self.connect_device)
        browser = ServiceBrowser(zeroconf, "_oscjson._tcp.local.", listener)
        self.current_remote = None

    def connect_device(self, device):
        self.panel.device = device

    def contextual_menu(self, position):

        indexes = self.explorer.selectedIndexes()
        if len(indexes) > 0:

            level = 0
            index = indexes[0]
            while index.parent().isValid():
                index = index.parent()
                level += 1
            node = self.devices_model.itemFromIndex(index)
            menu = QMenu()
            if level == 0:
                menu.addAction("Refresh Device Namespace", node.update)
            elif level > 0:
                menu.addAction("Refresh Node", node.update)
            menu.exec_(self.explorer.viewport().mapToGlobal(position))

    def selection_updated(self, *args, **kwargs):
        """
        called when device selection is updated
        we will disconnect our ossia.OSCQueryDevice from the previous device if there was one
        and then we will reconnect it to the current instance of the Device model
        """
        index = self.device_selection_model.selectedIndexes()
        # we consider unique selection
        modelIndex = index[0]
        if modelIndex:
            if self.current_remote:
                self.panel.layout.removeWidget(self.current_remote)
                self.current_remote.deleteLater()
                del self.current_remote
                self.current_remote = None
            node = self.devices_model.itemFromIndex(modelIndex).node
            if node.__class__.__name__ == 'OSCQueryDevice':
                print('Device')
            else:
                if node.parameter:
                    self.current_remote = self.panel.add_remote(node.parameter)
                else:
                    self.current_remote = self.panel.add_inspector(node)
        else:
            print('no node selected')
예제 #22
0
class AddToProject(QDialog):
    """Dialog to let the user choose one of the folders from the opened proj"""

    def __init__(self, projects, parent=None):
        super(AddToProject, self).__init__(parent)
        #pathProjects must be a list
        self._projects = projects
        self.setWindowTitle(translations.TR_ADD_FILE_TO_PROJECT)
        self.pathSelected = ''
        vbox = QVBoxLayout(self)

        hbox = QHBoxLayout()
        self._list = QListWidget()
        for project in self._projects:
            self._list.addItem(project.name)
        self._list.setCurrentRow(0)
        self._tree = QTreeView()
        #self._tree.header().setHidden(True)
        self._tree.setSelectionMode(QTreeView.SingleSelection)
        self._tree.setAnimated(True)
        self.load_tree(self._projects[0])
        hbox.addWidget(self._list)
        hbox.addWidget(self._tree)
        vbox.addLayout(hbox)

        hbox2 = QHBoxLayout()
        btnAdd = QPushButton(translations.TR_ADD_HERE)
        btnCancel = QPushButton(translations.TR_CANCEL)
        hbox2.addWidget(btnCancel)
        hbox2.addWidget(btnAdd)
        vbox.addLayout(hbox2)

        btnCancel.connect(self.close)
        btnAdd.connect(self._select_path)
        self._list.currentItemChanged['QTreeWidgetItem*', 'QTreeWidgetItem*'].connect(self._project_changed)

    def _project_changed(self, item, previous):
        #FIXME, this is not being called, at least in osx
        for each_project in self._projects:
            if each_project.name == item.text():
                self.load_tree(each_project)

    def load_tree(self, project):
        """Load the tree view on the right based on the project selected."""
        qfsm = QFileSystemModel()
        qfsm.setRootPath(project.path)
        load_index = qfsm.index(qfsm.rootPath())
        qfsm.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot)
        qfsm.setNameFilterDisables(False)
        pext = ["*{0}".format(x) for x in project.extensions]
        qfsm.setNameFilters(pext)

        self._tree.setModel(qfsm)
        self._tree.setRootIndex(load_index)

        t_header = self._tree.header()
        t_header.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        t_header.setSectionResizeMode(0, QHeaderView.Stretch)
        t_header.setStretchLastSection(False)
        t_header.setClickable(True)

        self._tree.hideColumn(1)  # Size
        self._tree.hideColumn(2)  # Type
        self._tree.hideColumn(3)  # Modification date

        #FIXME: Changing the name column's title requires some magic
        #Please look at the project tree

    def _select_path(self):
        """Set pathSelected to the folder selected in the tree."""
        path = self._tree.model().filePath(self._tree.currentIndex())
        if path:
            self.pathSelected = path
            self.close()
예제 #23
0
    from model import FileSystemTreeModel, ActionDelegate

if __name__ == "__main__":
    app = QApplication(sys.argv)

    model = FileSystemTreeModel(Node('Filename'), path='/')

    tree = QTreeView()
    tree.setModel(model)
    tree.setIconSize(QSize(48, 48))

    tree.setHeaderHidden(False)
    # tree.setColumnWidth(0, 250)
    tree.setColumnWidth(1, 30)

    hv = tree.header()
    hv.setStretchLastSection(False)
    hv.setSectionResizeMode(0, QHeaderView.Stretch)
    hv.setSectionResizeMode(1, QHeaderView.Fixed)

    tree.setWordWrap(True)
    tree.setTextElideMode(Qt.ElideNone)
    act_delegate = ActionDelegate()
    tree.setItemDelegateForColumn(1, act_delegate)
    # tree.setFixedSize(700, 900)
    tree.setMinimumSize(700, 900)

    @pyqtSlot('QPoint')
    def open_menu(pos):
        """
        :type pos: QPoint
class DriveAnalysisWidget(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle('Drive Analysis Tool')
        # self.root_path = os.path.expanduser('~')
        # self.root_path = os.path.expanduser('~\\Downloads')
        self.root_path = os.path.expanduser(
            os.path.join('~', 'Dropbox', 'mcgill'))
        self.root_path2 = ''
        self.dbx_json_dirpath = '/'
        self.have_two_roots = False
        self.threadpool = QThreadPool()
        self.expanded_items_list = []
        self.unchecked_items_list = []
        self.unchecked_items_set = set()
        self.renamed_items_dict = dict()

        # with open(os.path.expanduser(os.path.join('~', 'Dropbox', 'mcgill', 'File Zoomer',
        #                                           'code', 'drive_analysis_tool', 'dir_dict.pkl')), 'rb') as ddf:
        #     self.og_dir_dict = pickle.load(ddf)
        # self.anon_dir_dict = deepcopy(self.og_dir_dict)

        self.og_dir_dict, self.anon_dir_dict = dict(), dict()
        self.og_dir_dict2, self.anon_dir_dict2 = dict(), dict()
        self.user_folder_props = dict()
        self.user_folder_typical = True
        self.build_tree_structure_threaded(self.root_path)

        # test_btn = QPushButton()
        # test_btn.setText('Run tests')
        # test_btn.resize(test_btn.sizeHint())
        # test_btn.clicked.connect(self.test_script)

        select_btn = QPushButton('Select Root 1', self)
        select_btn.setToolTip(
            'Select <b>personal folder 1</b> for data collection.')
        select_btn.clicked.connect(self.show_file_dialog)
        select_btn.resize(select_btn.sizeHint())

        select_btn2 = QPushButton('Select Root 2', self)
        select_btn2.setToolTip(
            'Select <b>personal folder 2</b> (if present) for data collection.'
        )
        select_btn2.clicked.connect(self.show_file_dialog2)
        select_btn2.resize(select_btn2.sizeHint())

        preview_btn = QPushButton('Preview', self)
        preview_btn.setToolTip(
            'Preview folder data that will be used for research')
        preview_btn.clicked.connect(self.preview_anon_tree_threaded)
        preview_btn.resize(preview_btn.sizeHint())

        self.submit_btn = QPushButton('Submit', self)
        self.submit_btn.setToolTip('Submit encrypted folder data to the cloud')
        self.submit_btn.clicked.connect(self.upload_collected_data)
        self.submit_btn.resize(self.submit_btn.sizeHint())
        self.submit_btn.setEnabled(False)

        self.folder_edit = QLabel()
        self.folder_edit.setText(self.root_path)

        self.folder_edit2 = QLabel()
        self.folder_edit2.setText(self.root_path2)

        self.status_label = QLabel()
        self.status_label.setText('')
        self.status_label.setStyleSheet("color: red;" "font: bold;")
        self.status_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)

        self.user_folder_props_label = QLabel()
        self.user_folder_props_label.setAlignment(Qt.AlignCenter)
        self.user_folder_props_label.setText(
            'Characteristics of folder structure')

        self.user_folder_props_table = QTableWidget()
        self.user_folder_props_table.setRowCount(22)
        self.user_folder_props_table.setColumnCount(2)
        labels = [
            'Total files', 'Total folders', 'Greatest breadth of folder tree',
            'Average breadth of folder tree', '# folders at root',
            '# leaf folders (folders without subfolders)',
            '% leaf folders (folders without subfolders)',
            'Average depth of leaf folders (folders without subfolders)',
            '# switch folders (folders with subfolders and no files)',
            '% switch folders (folders with subfolders and no files)',
            'Average depth of switch folders (folders with subfolders and no files)',
            'Greatest depth where folders are found',
            'Folder waist (depth where folders are most commonly found)',
            'Average depth where folders are found',
            'Branching factor (average subfolders per folder, excepting leaf folders)',
            '# files at root', 'Average # files in folders', '# empty folders',
            '% empty folders', 'Average depth where files are found',
            'Depth where files are most commonly found',
            '# files at depth where files are most commonly found'
        ]
        label_keys = [
            'n_files', 'n_folders', 'breadth_max', 'breadth_mean',
            'root_n_folders', 'n_leaf_folders', 'pct_leaf_folders',
            'depth_leaf_folders_mean', 'n_switch_folders',
            'pct_switch_folders', 'depth_switch_folders_mean', 'depth_max',
            'depth_folders_mode', 'depth_folders_mean', 'branching_factor',
            'root_n_files', 'n_files_mean', 'n_empty_folders',
            'pct_empty_folders', 'depth_files_mean', 'depth_files_mode',
            'file_breadth_mode_n_files'
        ]
        for row, label, label_key in zip(range(22), labels, label_keys):
            label_item = QTableWidgetItem(label)
            label_item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            value_item = QTableWidgetItem('?')
            value_item.setData(Qt.UserRole, label_key)
            value_item.setTextAlignment(Qt.AlignRight)
            value_item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.user_folder_props_table.setItem(row, 0, label_item)
            self.user_folder_props_table.setItem(row, 1, value_item)
        self.user_folder_props_table.setHorizontalHeaderLabels(
            ['Property', 'Value'])
        self.user_folder_props_table.horizontalHeader().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.user_folder_props_table.horizontalHeader().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        # print(self.user_folder_props_table.item(0, 1).data(Qt.UserRole))

        # self.user_folder_typical_label = QLabel()
        # self.user_folder_typical_label.setText('')
        # self.user_folder_typical_label.setStyleSheet("font: bold;")
        # self.user_folder_typical_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)

        og_tree_label = QLabel()
        og_tree_label.setAlignment(Qt.AlignCenter)
        og_tree_label.setText('Original folders data')

        anon_tree_label = QLabel()
        anon_tree_label.setAlignment(Qt.AlignCenter)
        anon_tree_label.setText('Folders data to be used for research')

        self.og_tree = QTreeView()
        self.og_model = QStandardItemModel()
        self.og_tree.setModel(self.og_model)
        self.og_model.setHorizontalHeaderLabels(
            ['Folder Name', 'Renamed Folder', 'Number of Files'])
        self.og_root_item = self.og_model.invisibleRootItem()
        self.refresh_treeview(self.og_model, self.og_tree, self.og_dir_dict)
        self.og_tree.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.og_tree.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self.og_tree.header().setSectionResizeMode(
            2, QHeaderView.ResizeToContents)
        self.og_model.itemChanged.connect(self.on_item_change)

        self.anon_tree = QTreeView()
        self.anon_model = QStandardItemModel()
        self.anon_tree.setModel(self.anon_model)
        self.anon_model.setHorizontalHeaderLabels(
            ['Folder Name', 'Number of Files'])
        self.anon_root_item = self.anon_model.invisibleRootItem()
        self.refresh_treeview(self.anon_model,
                              self.anon_tree,
                              self.anon_dir_dict,
                              checkable=False,
                              anon_tree=True)
        self.anon_tree.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.anon_tree.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)

        grid = QGridLayout()
        grid.addWidget(select_btn, 0, 0, 1, 1)
        grid.addWidget(self.folder_edit, 0, 1, 1, 7)
        grid.addWidget(select_btn2, 1, 0, 1, 1)
        grid.addWidget(self.folder_edit2, 1, 1, 1, 7)
        # grid.addWidget(self.status_label, 1, 0, 1, 8)
        grid.addWidget(og_tree_label, 2, 0, 1, 5)
        grid.addWidget(anon_tree_label, 2, 5, 1, 3)
        grid.addWidget(self.og_tree, 3, 0, 1, 5)
        grid.addWidget(self.anon_tree, 3, 5, 1, 3)
        grid.addWidget(self.user_folder_props_label, 4, 0, 1, 8)
        grid.addWidget(self.user_folder_props_table, 5, 0, 2, 8)
        # grid.addWidget(self.user_folder_typical_label, 7, 0, 1, 6)
        grid.addWidget(self.status_label, 8, 0, 1, 6)
        grid.addWidget(preview_btn, 8, 6, 1, 1)
        grid.addWidget(self.submit_btn, 8, 7, 1, 1)

        self.setLayout(grid)
        self.resize(1280, 720)
        self.show()

    def refresh_treeview(self,
                         model,
                         tree,
                         dir_dict,
                         checkable=True,
                         anon_tree=False,
                         append=False):
        if not append:
            model.removeRow(0)
        root_item = model.invisibleRootItem()
        self.append_all_children(
            1, dir_dict, root_item, checkable,
            anon_tree)  # dir_dict key starts at 1 since 0==False
        # tree.setModel(model)
        tree.expandToDepth(0)

    def append_all_children(self,
                            dirkey,
                            dir_dict,
                            parent_item,
                            checkable=True,
                            anon_tree=False):
        if dirkey in dir_dict:
            dirname = QStandardItem(dir_dict[dirkey]['dirname'])
            dirname_edited = QStandardItem(dir_dict[dirkey]['dirname'])
            nfiles = QStandardItem(str(dir_dict[dirkey]['nfiles']))
            if anon_tree:
                items = [dirname, nfiles]
            else:
                items = [dirname, dirname_edited, nfiles]
            dirname.setData(dirkey, Qt.UserRole)
            dirname_edited.setData(dirkey, Qt.UserRole)
            if checkable:
                dirname.setFlags(Qt.ItemIsEnabled | Qt.ItemIsUserTristate
                                 | Qt.ItemIsUserCheckable)
                dirname.setCheckState(Qt.Checked)
                dirname_edited.setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable)
                nfiles.setFlags(Qt.ItemIsEnabled)
            parent_item.appendRow(items)
            child_ix = parent_item.rowCount() - 1
            parent_item = parent_item.child(child_ix)
            children_keys = dir_dict[dirkey]['childkeys']
            for child_key in sorted(children_keys):
                self.append_all_children(child_key, dir_dict, parent_item,
                                         checkable, anon_tree)

    def on_item_change(self, item):
        if item.column() == 0:
            dirkey = item.data(Qt.UserRole)
            if item.rowCount() == 0 and item.checkState(
            ) == Qt.PartiallyChecked:
                item.setCheckState(Qt.Checked)
            item_checkstate = item.checkState()
            parent_item = item.parent()
            if parent_item is None:
                nchild = item.rowCount()
                if nchild > 0:
                    for child_ix in range(nchild):
                        self.propagate_checkstate_child(
                            item, child_ix, item_checkstate)
            if parent_item is not None:
                child_ix = item.row()
                self.propagate_checkstate_child(parent_item, child_ix,
                                                item_checkstate)
                self.propagate_checkstate_parent(item, item_checkstate)
            # self.unchecked_items_list = []
            # self.list_unchecked(self.og_root_item, 0, self.unchecked_items_list)
            # print(self.unchecked_items_list)
            if item_checkstate == Qt.Unchecked:
                self.unchecked_items_set.add(dirkey)
                # if dirkey in self.renamed_items_dict:
                #     self.renamed_items_dict.pop(dirkey)
            elif item_checkstate in (Qt.Checked, Qt.PartiallyChecked):
                if dirkey in self.unchecked_items_set:
                    self.unchecked_items_set.remove(dirkey)
            self.status_label.setText('Click \'Preview\' to see changes')
        if item.column() == 1:
            dirkey = item.data(Qt.UserRole)
            self.renamed_items_dict[dirkey] = item.text()
            self.status_label.setText('Click \'Preview\' to see changes')

    def propagate_checkstate_child(self, parent_item, child_ix,
                                   parent_checkstate):
        if parent_checkstate != Qt.PartiallyChecked:
            parent_item.child(child_ix).setCheckState(parent_checkstate)
            parent_item = parent_item.child(child_ix)
            nchild = parent_item.rowCount()
            if nchild > 0:
                for child_ix in range(nchild):
                    self.propagate_checkstate_child(parent_item, child_ix,
                                                    parent_checkstate)

    def propagate_checkstate_parent(self, item, item_checkstate):
        parent_item = item.parent()
        if parent_item is not None:
            if self.all_sibling_checked(item):
                parent_item.setCheckState(Qt.Checked)
            if item_checkstate in (
                    Qt.Checked, Qt.PartiallyChecked
            ) and parent_item.checkState() == Qt.Unchecked:
                parent_item.setCheckState(Qt.PartiallyChecked)
            if item_checkstate in (
                    Qt.Unchecked, Qt.PartiallyChecked
            ) and parent_item.checkState() == Qt.Checked:
                parent_item.setCheckState(Qt.PartiallyChecked)

    def all_sibling_checked(self, item):
        all_checked = True
        if item.parent() is not None:
            parent_item = item.parent()
            nchild = parent_item.rowCount()
            for child_ix in range(nchild):
                if parent_item.child(child_ix).checkState() in (
                        Qt.Unchecked, Qt.PartiallyChecked):
                    all_checked = False
                    break
        return all_checked

    def expand_items(self, tree, parent_item, child_ix, expanded_items):
        item = parent_item.child(child_ix)
        if item.data(Qt.UserRole) in expanded_items:
            tree.setExpanded(item.index(), True)
        parent_item = parent_item.child(child_ix)
        nchild = parent_item.rowCount()
        if nchild > 0:
            for child_ix in range(nchild):
                self.expand_items(tree, parent_item, child_ix, expanded_items)

    def list_expanded(self, tree, parent_item, child_ix, expanded_items):
        # print(type(parent_item.child(0)))
        item = parent_item.child(child_ix)
        if tree.isExpanded(item.index()):
            expanded_items.append(item.data(Qt.UserRole))
        parent_item = parent_item.child(child_ix)
        nchild = parent_item.rowCount()
        if nchild > 0:
            for child_ix in range(nchild):
                self.list_expanded(tree, parent_item, child_ix, expanded_items)

    def list_unchecked(self, parent_item, child_ix, unchecked_items):
        item = parent_item.child(child_ix)
        if item.checkState() == Qt.Unchecked:
            unchecked_items.append(item.data(Qt.UserRole))
        parent_item = parent_item.child(child_ix)
        nchild = parent_item.rowCount()
        if nchild > 0:
            for child_ix in range(nchild):
                self.list_unchecked(parent_item, child_ix, unchecked_items)

    def on_item_change_threaded(self, item):
        worker = Worker(self.on_item_change, item)
        worker.signals.started.connect(self.on_item_change_started)
        worker.signals.result.connect(self.on_item_change_finished)
        self.threadpool.start(worker)

    def on_item_change_started(self):
        self.status_label.setText('Refreshing tree, please wait...')

    def on_item_change_finished(self):
        self.status_label.setText('')

    def build_tree_structure_threaded(self,
                                      root_path,
                                      root_ix=1,
                                      append_to_tree=False):
        worker = Worker(record_stat, root_path)
        worker.signals.started.connect(self.build_tree_started)
        worker.signals.result.connect(self.build_tree_midway)
        worker.signals.finished.connect(
            lambda: self.build_tree_finished(append_to_tree))
        self.threadpool.start(worker)

    def build_tree_started(self):
        self.status_label.setText('Building tree, please wait...')

    def build_tree_midway(self, result):
        self.og_dir_dict = result
        # self.anon_dir_dict = deepcopy(self.og_dir_dict)
        self.anon_dir_dict = _pickle.loads(_pickle.dumps(self.og_dir_dict))

    def build_tree_finished(self, append_to_tree):
        self.refresh_treeview(self.og_model,
                              self.og_tree,
                              self.og_dir_dict,
                              append=append_to_tree)
        self.refresh_treeview(self.anon_model,
                              self.anon_tree,
                              self.anon_dir_dict,
                              checkable=False,
                              anon_tree=True,
                              append=append_to_tree)
        self.status_label.setText('Click \'Preview\' to see changes')

    def preview_anon_tree(self):
        start = time.time()
        # self.anon_dir_dict = deepcopy(self.og_dir_dict)
        self.anon_dir_dict = _pickle.loads(_pickle.dumps(self.og_dir_dict))
        print(start - time.time())
        start = time.time()
        self.anon_dir_dict = anonymize_stat(self.anon_dir_dict,
                                            self.unchecked_items_set,
                                            self.renamed_items_dict)
        print(start - time.time())
        start = time.time()
        self.refresh_treeview(self.anon_model,
                              self.anon_tree,
                              self.anon_dir_dict,
                              checkable=False,
                              anon_tree=True)
        print(start - time.time())
        start = time.time()
        self.expanded_items_list = []
        self.list_expanded(self.og_tree, self.og_root_item, 0,
                           self.expanded_items_list)
        self.expand_items(self.anon_tree, self.anon_root_item, 0,
                          self.expanded_items_list)
        print(start - time.time())

    def preview_anon_tree_threaded(self):
        worker = Worker(self.preview_anon_tree)
        worker.signals.started.connect(self.preview_anon_tree_started)
        worker.signals.result.connect(self.preview_anon_tree_finished)
        self.threadpool.start(worker)

    def preview_anon_tree_started(self):
        self.status_label.setText('Constructing preview tree, please wait...')

    def preview_anon_tree_finished(self):
        # self.status_label.setText('')
        self.display_user_folder_props()

    def display_user_folder_props(self):
        self.user_folder_props = drive_measurement(self.anon_dir_dict)
        self.user_folder_typical = check_collection_properties(
            self.user_folder_props)
        for row in range(22):
            label_key = self.user_folder_props_table.item(row,
                                                          1).data(Qt.UserRole)
            value_item = QTableWidgetItem(
                str(round(self.user_folder_props[label_key], 1)))
            value_item.setData(Qt.UserRole, label_key)
            value_item.setTextAlignment(Qt.AlignRight)
            value_item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
            self.user_folder_props_table.setItem(row, 1, value_item)
        self.user_folder_props_table.reset()
        is_typical = self.user_folder_typical
        # is_typical = True
        if is_typical:
            self.submit_btn.setEnabled(True)
            is_typical_str = 'Values in nominal range, submit?'
        elif not is_typical:
            is_typical_str = 'Values are atypical, data not acceptable for submission'
        # self.user_folder_typical_label.setText(is_typical_str)
        self.status_label.setText(is_typical_str)

    def show_file_dialog(self):
        dirpath = QFileDialog.getExistingDirectory(self, 'Select Folder',
                                                   self.root_path)
        if dirpath:
            self.root_path = os.path.abspath(dirpath)
            self.folder_edit.setText(self.root_path)
            self.build_tree_structure_threaded(self.root_path)

    def show_file_dialog2(self):
        dirpath = QFileDialog.getExistingDirectory(self, 'Select Folder',
                                                   self.root_path)
        if dirpath:
            dirpath = os.path.abspath(dirpath)
            if Path(self.root_path) in Path(dirpath).parents:
                self.status_label.setText(
                    'Root folder 1 is a parent of root folder 2. '
                    'Navigate the existing tree to find root folder 2.')
            elif Path(dirpath) in Path(self.root_path).parents:
                self.status_label.setText(
                    'Root folder 2 is a parent of root folder 1. '
                    'Change root folder 1 to root folder 2 through '
                    '\'Select Root 1\'.')
            else:
                self.have_two_roots = True
                self.root_path2 = dirpath
                self.folder_edit2.setText(self.root_path2)
                self.build_tree_structure_threaded(self.root_path2,
                                                   append_to_tree=True)

    def upload_collected_data(self):
        data = bytes(json.dumps(self.anon_dir_dict), 'utf8')
        data = compress_data(data)
        encrypted_json, encrypted_jsonkey = encrypt_data(data)
        dropbox_upload(
            encrypted_json,
            generate_filename(self.dbx_json_dirpath, suffix='_dir_dict.enc'))
        dropbox_upload(
            encrypted_jsonkey,
            generate_filename(self.dbx_json_dirpath, suffix='_sym_key.enc'))
        self.status_label.setText('Data uploaded. Thanks!')

    def test_script(self):
        unchecked_items_list = []
        self.list_unchecked(self.root_item, 0, unchecked_items_list)
        print(
            set(self.og_dir_dict.keys()).difference(self.anon_dir_dict.keys()))
        print(unchecked_items_list)
예제 #25
0
class Installed(preferences.Group):
    """Overview of installed extensions.

    A QTreeView lists the metadata of all installed extensions.
    If the currently selected extension provides a configuration
    widget it is displayed in the bottom group of the page.

    With a checkbox individual extensions can be deactivated.
    Metadata is listed for all *installed* extensions, regardless
    of manual deactivation or load failure.
    """

    def __init__(self, page):
        super(Installed, self).__init__(page)

        layout = QVBoxLayout()
        self.setLayout(layout)

        # This must be called before self.populate() because
        # the data model relies on the labels
        app.translateUI(self)

        self.tree = QTreeView()
        self.name_items = {}
        self._selected_extension = ''
        self.tree.setModel(QStandardItemModel())
        self.tree.model().setColumnCount(2)
        self.tree.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.tree.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.tree.setHeaderHidden(True)
        self.tree.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.tree.selectionModel().selectionChanged.connect(
            self.selection_changed)
        self.populate()
        layout.addWidget(self.tree)

    def translateUI(self):
        self.setTitle(_("Installed Extensions"))
        self.config_labels = {
            'extension-name': _("Name"),
            'maintainers': _("Maintainer(s)"),
            'version': _("Version"),
            'api-version': _("API version"),
            'license': _("License"),
            'short-description': _("Short Description"),
            'description': _("Description"),
            'repository': _("Repository"),
            'website': _("Website"),
            'dependencies': _("Dependencies")
        }

    def loadSettings(self):
        s = QSettings()
        self.setEnabled(self.page().active())
        s.beginGroup("extension-settings/installed")
        inactive = s.value("inactive", [], list)
        for ext in self.name_items.keys():
            self.name_items[ext].setCheckState(
                Qt.Checked if ext not in inactive else Qt.Unchecked)
        self.tree.model().dataChanged.connect(self.page().changed)

    def saveSettings(self):
        s = QSettings()
        s.beginGroup("extension-settings/installed")
        inactive = [ext for ext in self.name_items.keys()
            if self.name_items[ext].checkState() == Qt.Unchecked]
        s.setValue("inactive", inactive)

    def populate(self):
        """Populate the tree view with data from the installed extensions.
        """

        # TODO/Question:
        # Would it make sense to move this to a dedicated model class
        # complementing the FailedModel?

        root = self.tree.model().invisibleRootItem()
        extensions = app.extensions()
        for ext in extensions.installed_extensions():
            ext_infos = extensions.infos(ext)
            display_name = ext_infos.get(ext, ext) if ext_infos else ext.name()
            loaded_extension = extensions.get(ext)
            if loaded_extension:
                display_name += ' ({})'.format(loaded_extension.load_time())

            name_item = QStandardItem(display_name)
            name_item.extension_name = ext
            name_item.setCheckable(True)
            self.name_items[ext] = name_item
            icon = extensions.icon(ext)
            if icon:
                name_item.setIcon(icon)
            root.appendRow([name_item])
            for entry in [
                'extension-name',
                'short-description',
                'description',
                'version',
                'api-version',
                'dependencies',
                'maintainers',
                'repository',
                'website',
                'license'
            ]:
                label_item = QStandardItem('{}:'.format(
                    self.config_labels[entry]))
                label_item.setTextAlignment(Qt.AlignTop)
                bold = QFont()
                bold.setWeight(QFont.Bold)
                label_item.setFont(bold)
                details = ext_infos.get(entry, "") if ext_infos else ""
                if type(details) == list:
                    details = '\n'.join(details)
                details_item = QStandardItem(details)
                details_item.setTextAlignment(Qt.AlignTop)
                if entry == 'api-version':
                    # Check for correct(ly formatted) api-version entry
                    # and highlight it in case of mismatch
                    api_version = appinfo.extension_api
                    if not details:
                        details_item.setFont(bold)
                        details_item.setText(
                            _("Misformat: {api}").format(details))
                    elif not details == api_version:
                            details_item.setFont(bold)
                            details_item.setText('{} ({}: {})'.format(
                                details,
                                appinfo.appname,
                                api_version))
                name_item.appendRow([label_item, details_item])

    def selected_extension(self):
        """Return the (directory) name of the extension that
        is currently selected."""
        return self._selected_extension

    def selection_changed(self, new, old):
        """Show the configuration widget for the selected extension,
        if available."""
        config = self.siblingGroup(Config)
        if new.indexes():
            ext_item = self.tree.model().itemFromIndex(new.indexes()[0])
            # NOTE: This may have to be changed if there should be
            # more complexity in the tree model than now (a selected
            # row is either a top-level row or its immediate child)
            if not hasattr(ext_item, 'extension_name'):
                ext_item = ext_item.parent()
            name = ext_item.extension_name
            if name == self.selected_extension():
                return
        else:
            # If nothing is selected, show the "empty" widget
            name = ""

        config.hide_extension()
        self._selected_extension = name
        config.show_extension(name)
예제 #26
0
class Dialog_ImageFolder():
    def __init__(self, parent, title, init_path):
        self.w = QDialog(parent)

        self.parent = parent
        self.left = 300
        self.top = 300
        self.width = 600
        self.height = 400
        self.title = title

        self.dirModel = QFileSystemModel()
        self.dirModel.setRootPath(init_path)
        self.dirModel.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs)
        self.treeview = QTreeView()
        self.treeview.setModel(self.dirModel)
        self.treeview.setRootIndex(self.dirModel.index(""))
        self.treeview.clicked.connect(self.on_clicked)
        #--- Hide All Header Sections Except First ----
        header = self.treeview.header()
        for sec in range(1, header.count()):
            header.setSectionHidden(sec, True)
        #--- ---- ---- ---- ---- ---- ---- ---- ---- --

        focus_index = self.dirModel.index(init_path)
        self.treeview.setCurrentIndex(focus_index)
        self.current_row_changed()

        self.listview = QListView()
        self.listview.setViewMode(QListView.IconMode)
        self.listview.setIconSize(QSize(192, 192))

        targetfiles1 = glob.glob(os.path.join(init_path, '*.png'))
        targetfiles2 = glob.glob(os.path.join(init_path, '*.tif'))
        targetfiles3 = glob.glob(os.path.join(init_path, '*.tiff'))
        targetfiles = targetfiles1 + targetfiles2 + targetfiles3
        lm = _MyListModel(targetfiles, self.parent)
        self.listview.setModel(lm)

        self.sub_layout = QHBoxLayout()
        self.sub_layout.addWidget(self.treeview)
        self.sub_layout.addWidget(self.listview)

        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open
                                          | QDialogButtonBox.Cancel)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        self.main_layout = QVBoxLayout()
        self.main_layout.addLayout(self.sub_layout)
        self.main_layout.addWidget(self.buttonBox)

        self.w.setGeometry(self.left, self.top, self.width, self.height)
        self.w.setWindowTitle(self.title)
        self.w.setWindowIcon(QIcon(os.path.join(icon_dir, 'Mojo2_16.png')))
        self.w.setLayout(self.main_layout)

    def current_row_changed(self):
        index = self.treeview.currentIndex()
        self.treeview.scrollTo(index, QAbstractItemView.EnsureVisible)
        self.treeview.resizeColumnToContents(0)

    def on_clicked(self, index):
        path = self.dirModel.fileInfo(index).absoluteFilePath()

        targetfiles1 = glob.glob(os.path.join(path, '*.png'))
        targetfiles2 = glob.glob(os.path.join(path, '*.tif'))
        targetfiles3 = glob.glob(os.path.join(path, '*.tiff'))
        targetfiles = targetfiles1 + targetfiles2 + targetfiles3

        lm = _MyListModel(targetfiles, self.parent)
        self.listview.setModel(lm)

    def accept(self):
        index = self.treeview.currentIndex()
        self.newdir = self.dirModel.filePath(index)
        self.w.done(1)

    def reject(self):
        self.w.done(0)

    def GetValue(self):
        index = self.treeview.currentIndex()
        self.newdir = self.dirModel.filePath(index)
        return self.newdir
예제 #27
0
class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.setMinimumSize(QSize(480, 80))
        self.setWindowTitle("PyQtSample")
        central_widget = QWidget(self)
        self.setCentralWidget(central_widget)

        hbox_layout = QHBoxLayout(self)
        central_widget.setLayout(hbox_layout)

        buttons_background = QWidget(self)
        buttons_layout = QHBoxLayout()
        buttons_background.setLayout(buttons_layout)

        self.add_button = QPushButton("Add")
        buttons_layout.addWidget(self.add_button)

        self.remove_button = QPushButton("Remove")
        buttons_layout.addWidget(self.remove_button)

        tree_layout = QVBoxLayout(self)
        self.tree_view = QTreeView()
        self.tree_view.header().hide()
        self.tree_view.setMaximumWidth(300)
        self.tree_model = ObjectsModel.TreeModel()
        self.tree_view.setModel(self.tree_model)
        tree_layout.addWidget(buttons_background)
        tree_layout.addWidget(self.tree_view)

        hbox_layout.addLayout(tree_layout)

        self.graphics_view = QGraphicsView()
        self.scene = QGraphicsScene()
        self.graphics_view.setScene(self.scene)
        hbox_layout.addWidget(self.graphics_view)

        self.properties_view = QTableView()
        self.properties_view.setMaximumWidth(300)
        self.properties_model = PropertiesModel.TableModel()
        self.properties_view.setModel(self.properties_model)
        hbox_layout.addWidget(self.properties_view)

        self.init_menu()
        self.test()
        self.connectSignals()

    def connectSignals(self):
        self.add_button.clicked.connect(self.onAddClicked)
        self.remove_button.clicked.connect(self.onRemoveClicked)
        self.tree_view.clicked.connect(self.onClicked)
        self.properties_model.dataChanged.connect(self.onPropertyChanged)

    def onPropertyChanged(self):
        self.rebuildModel()

    def rebuildModel(self):
        #save index hierarhy
        indexes = []
        tmp_index = self.tree_view.currentIndex()
        indexes.append(tmp_index)
        while tmp_index.parent().isValid():
            indexes.append(tmp_index.parent())
            tmp_index = tmp_index.parent()

        self.tree_model.initRoot(self.items)
        self.tree_view.expandAll()

        if len(indexes) == 0:
            self.onClicked(self.tree_model.index(0, 0))
        else:
            last_index = indexes.pop(-1)
            index = self.tree_model.index(last_index.row(),
                                          last_index.column())
            #restore index hierarchy
            while len(indexes) > 0:
                last_index = indexes.pop(-1)
                index = self.tree_model.index(last_index.row(),
                                              last_index.column(), index)

            if index.isValid():
                self.onClicked(index)
            else:
                self.onClicked(self.tree_model.index(0, 0))

    def init_menu(self):
        exit_action = QAction("&Exit", self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.triggered.connect(qApp.quit)
        file_menu = self.menuBar().addMenu("&File")
        file_menu.addAction(QAction("Open", self))
        file_menu.addAction(QAction("Save", self))
        file_menu.addAction(QAction("SaveAs", self))
        file_menu.addSeparator()
        file_menu.addAction(exit_action)

    def appendObjectOnScene(self, object=DataStructures.Object):
        if object.rect.isValid():
            self.scene.addRect(object.rect, object.color)
        for child in object.childrens:
            self.appendObjectOnScene(child)

    def onClicked(self, index):
        object = index.data(Qt.UserRole + 1)

        self.properties_model.initProperties(object)

        self.scene.clear()
        self.appendObjectOnScene(object)
        self.tree_view.setCurrentIndex(index)

    def onAddClicked(self):
        index = self.tree_view.currentIndex()
        print(index)
        object = index.data(Qt.UserRole + 1)
        print(object.description())
        object.add_children(DataStructures.Object("New item"))
        self.rebuildModel()

    def onRemoveClicked(self):
        index = self.tree_view.currentIndex()
        object = index.data(Qt.UserRole + 1)
        if not object.parent:
            self.items.remove(object)
        else:
            object.parent.childrens.remove(object)

        self.rebuildModel()

    def test(self):
        self.items = []
        static = DataStructures.Object(
            "Static", DataStructures.createRect(0, 0, 800, 200))
        static.add_children(DataStructures.Object("child_1"))
        static.add_children(DataStructures.Object("child_2"))
        static.add_children(DataStructures.Object("child_3"))
        static.color = QColor(200, 0, 0).name()
        static.childrens[0].add_children(
            DataStructures.Object("child_1.1",
                                  DataStructures.createRect(40, 40, 80, 40)))

        self.items.append(static)

        dynamic = DataStructures.Object(
            "Dynamic", DataStructures.createRect(0, 0, 200, 800))
        dynamic.add_children(DataStructures.Object("child_1"))
        dynamic.add_children(DataStructures.Object("child_2"))
        dynamic.add_children(DataStructures.Object("child_3"))
        dynamic.childrens[2].add_children(DataStructures.Object("child_2.1"))
        dynamic.color = QColor(0, 0, 200).name()
        self.items.append(dynamic)

        self.rebuildModel()
예제 #28
0
class mainform(QWidget):
    """
    Главное окно, оно будет пока и главным классом приложения
    """
    def __init__(self):
        QWidget.__init__(self)
        self.resize(600, 400)
        self.connect()
        #запущаем соединение



    def connect(self):
#http://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html
#http://ftp.ics.uci.edu/pub/centos0/ics-custom-build/BUILD/PyQt-x11-gpl-4.7.2/examples/itemviews/simpletreemodel/simpletreemodel.py

        con = qsq.QSqlDatabase.addDatabase("QSQLITE", 'Base') #  делаем подключение к БД
        con.setDatabaseName("fn.sqlite") #  устанавливаем имя базы

        if not con.open(): #  если не открылось
            print ("База данных не открылась!")
            print ("-"+con.lastError().text()+"-")
            print (str(con.lastError().type()))
            return

        # cur = qsq.QSqlQuery(con) #  это прямое открытие
        # cur.exec("SELECT * FROM cases")
        # print (cur.lastError().text())

        #self.view = QTableView()  # создаём табличный вид
        self.view = QTreeView()

        self.model2 = TableToTreeModel2(self, con)  # создаём модельку - стандартную для БД, табличную
        self.view.setModel(self.model2)  # устанавливаем модель для вида
        self.model2.setTable("cases")  # устанавливаем таблицу и селектим из неё всё
        self.model2.select() # вот на этом этапе модель заполняется данными


        self.view.header().moveSection(11,1)
        self.view.header().moveSection(12,2)
        self.view.header().moveSection(13,3)



        self.view.hideColumn(1)
        self.view.hideColumn(6)
        self.view.hideColumn(7)
        self.view.hideColumn(8)
        #self.view.hideColumn(9)
        self.view.header().hideSection(9)
        #


#        print (self.model2.rowCount()) # возвращает количество строк

        # for i in range (self.model2.rowCount()):
        #     print (self.model2.data ( self.model2.index(i,1) ))

        #self.model2.setFilter('_id>1')  # установка фильтра на селект
        #self.model2.setFilter('')  # и снятие оного

        #print (self.model2.record(0).value('_shortText')) #  так можно получить данные
        #print (self.model2.index(0,0))
#        print (self.model2.data ( self.model2.index(0,0) ))

        self.layout = QVBoxLayout()  # пихаем вид в интерфейс
        self.layout.addWidget(self.view)
        self.setLayout(self.layout)

        con.close()  # закрываем соединение
예제 #29
0
파일: model.py 프로젝트: Zubax/kucher
def _unittest_register_tree_model():
    import gc
    from PyQt5.QtWidgets import (
        QApplication,
        QMainWindow,
        QTreeView,
        QHeaderView,
        QStyleOptionViewItem,
    )
    from ._mock_registers import get_mock_registers
    from .editor_delegate import EditorDelegate
    from .style_option_modifying_delegate import StyleOptionModifyingDelegate

    app = QApplication([])
    win = QMainWindow()

    tw = QTreeView(win)
    tw.setItemDelegate(EditorDelegate(tw, lambda s: print("Editor display:", s)))
    tw.setItemDelegateForColumn(
        0,
        StyleOptionModifyingDelegate(
            tw, decoration_position=QStyleOptionViewItem.Right
        ),
    )
    tw.setStyleSheet(
        """
    QTreeView::item { padding: 0 5px; }
    """
    )

    header: QHeaderView = tw.header()
    header.setSectionResizeMode(QHeaderView.ResizeToContents)
    header.setStretchLastSection(
        False
    )  # Horizontal scroll bar doesn't work if this is enabled

    registers = get_mock_registers()
    for r in registers:
        assert r.update_event.num_handlers == 0

    model = Model(win, registers)
    tw.setModel(model)

    for r in registers:
        assert r.update_event.num_handlers == 1

    win.setCentralWidget(tw)
    win.show()

    good_night_sweet_prince = False

    async def run_events():
        while not good_night_sweet_prince:
            app.processEvents()
            await asyncio.sleep(0.01)

    async def walk():
        nonlocal good_night_sweet_prince
        await asyncio.sleep(5)
        good_night_sweet_prince = True

    asyncio.get_event_loop().run_until_complete(asyncio.gather(run_events(), walk()))

    win.close()

    # At the very end, making sure that our registers do not keep the model alive via weak callback references
    del tw
    del win
    del app
    gc.collect()

    print("Model references:", gc.get_referrers(model))
    del model
    gc.collect()

    for r in registers:
        assert r.update_event.num_handlers == 1
        r.update_event.emit(r)
        assert r.update_event.num_handlers == 0
예제 #30
0
class AddBookmarkDialog(QDialog, Ui_AddBookmarkDialog):
    """
    Class implementing a dialog to add a bookmark or a bookmark folder.
    """
    def __init__(self, parent=None, bookmarksManager=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        @param bookmarksManager reference to the bookmarks manager
            object (BookmarksManager)
        """
        super(AddBookmarkDialog, self).__init__(parent)
        self.setupUi(self)
        
        self.__bookmarksManager = bookmarksManager
        self.__addedNode = None
        self.__addFolder = False
        
        if self.__bookmarksManager is None:
            import Helpviewer.HelpWindow
            self.__bookmarksManager = \
                Helpviewer.HelpWindow.HelpWindow.bookmarksManager()
        
        self.__proxyModel = AddBookmarkProxyModel(self)
        model = self.__bookmarksManager.bookmarksModel()
        self.__proxyModel.setSourceModel(model)
        
        self.__treeView = QTreeView(self)
        self.__treeView.setModel(self.__proxyModel)
        self.__treeView.expandAll()
        self.__treeView.header().setStretchLastSection(True)
        self.__treeView.header().hide()
        self.__treeView.setItemsExpandable(False)
        self.__treeView.setRootIsDecorated(False)
        self.__treeView.setIndentation(10)
        self.__treeView.show()
        
        self.locationCombo.setModel(self.__proxyModel)
        self.locationCombo.setView(self.__treeView)
        
        self.addressEdit.setInactiveText(self.tr("Url"))
        self.nameEdit.setInactiveText(self.tr("Title"))
        
        self.resize(self.sizeHint())
    
    def setUrl(self, url):
        """
        Public slot to set the URL of the new bookmark.
        
        @param url URL of the bookmark (string)
        """
        self.addressEdit.setText(url)
        self.resize(self.sizeHint())
    
    def url(self):
        """
        Public method to get the URL of the bookmark.
        
        @return URL of the bookmark (string)
        """
        return self.addressEdit.text()
    
    def setTitle(self, title):
        """
        Public method to set the title of the new bookmark.
        
        @param title title of the bookmark (string)
        """
        self.nameEdit.setText(title)
    
    def title(self):
        """
        Public method to get the title of the bookmark.
        
        @return title of the bookmark (string)
        """
        return self.nameEdit.text()
    
    def setDescription(self, description):
        """
        Public method to set the description of the new bookmark.
        
        @param description description of the bookamrk (string)
        """
        self.descriptionEdit.setPlainText(description)
    
    def description(self):
        """
        Public method to get the description of the bookmark.
        
        @return description of the bookamrk (string)
        """
        return self.descriptionEdit.toPlainText()
    
    def setCurrentIndex(self, idx):
        """
        Public method to set the current index.
        
        @param idx current index to be set (QModelIndex)
        """
        proxyIndex = self.__proxyModel.mapFromSource(idx)
        self.__treeView.setCurrentIndex(proxyIndex)
        self.locationCombo.setCurrentIndex(proxyIndex.row())
    
    def currentIndex(self):
        """
        Public method to get the current index.
        
        @return current index (QModelIndex)
        """
        idx = self.locationCombo.view().currentIndex()
        idx = self.__proxyModel.mapToSource(idx)
        return idx
    
    def setFolder(self, folder):
        """
        Public method to set the dialog to "Add Folder" mode.
        
        @param folder flag indicating "Add Folder" mode (boolean)
        """
        self.__addFolder = folder
        
        if folder:
            self.setWindowTitle(self.tr("Add Folder"))
            self.addressEdit.setVisible(False)
        else:
            self.setWindowTitle(self.tr("Add Bookmark"))
            self.addressEdit.setVisible(True)
        
        self.resize(self.sizeHint())
    
    def isFolder(self):
        """
        Public method to test, if the dialog is in "Add Folder" mode.
        
        @return flag indicating "Add Folder" mode (boolean)
        """
        return self.__addFolder
    
    def addedNode(self):
        """
        Public method to get a reference to the added bookmark node.
        
        @return reference to the added bookmark node (BookmarkNode)
        """
        return self.__addedNode
    
    def accept(self):
        """
        Public slot handling the acceptance of the dialog.
        """
        if (not self.__addFolder and not self.addressEdit.text()) or \
           not self.nameEdit.text():
            super(AddBookmarkDialog, self).accept()
            return
        
        from .BookmarkNode import BookmarkNode
        
        idx = self.currentIndex()
        if not idx.isValid():
            idx = self.__bookmarksManager.bookmarksModel().index(0, 0)
        parent = self.__bookmarksManager.bookmarksModel().node(idx)
        
        if self.__addFolder:
            type_ = BookmarkNode.Folder
        else:
            type_ = BookmarkNode.Bookmark
        bookmark = BookmarkNode(type_)
        bookmark.title = self.nameEdit.text()
        if not self.__addFolder:
            bookmark.url = self.addressEdit.text()
        bookmark.desc = self.descriptionEdit.toPlainText()
        
        self.__bookmarksManager.addBookmark(parent, bookmark)
        self.__addedNode = bookmark
        
        super(AddBookmarkDialog, self).accept()
예제 #31
0
class ChessClaimView(QMainWindow):
    """ The main window of the application.
    Attributes:
        rowCount(int): The number of the row the TreeView Table has.
        iconsSize(int): The recommended size of the icons.
        mac_notification: Notification for macOS
        win_notification: Notification for windows OS
    """
    def __init__(self):
        super().__init__()

        self.resize(720, 275)
        self.iconsSize = 16
        self.setWindowTitle('Chess Claim Tool')
        self.center()

        self.rowCount = 0

        if (platform.system() == "Darwin"):
            from MacNotification import Notification
            self.mac_notification = Notification()
        elif (platform.system() == "Windows"):
            from win10toast import ToastNotifier
            self.win_notification = ToastNotifier()

    def center(self):
        """ Centers the window on the screen """
        screen = QDesktopWidget().screenGeometry()
        size = self.geometry()
        self.move((screen.width()-size.width())/2,
            (screen.height()-size.height())/2)

    def set_GUI(self):
        """ Initialize GUI components. """

        # Create the Menu
        self.livePgnOption = QAction('Live PGN',self)
        self.livePgnOption.setCheckable(True)
        aboutAction = QAction('About',self)

        menubar = self.menuBar()

        optionsMenu = menubar.addMenu('&Options')
        optionsMenu.addAction(self.livePgnOption)

        aboutMenu = menubar.addMenu('&Help')
        aboutMenu.addAction(aboutAction)

        aboutAction.triggered.connect(self.slots.on_about_clicked)

        # Create the Claims Table (TreeView)
        self.claimsTable = QTreeView()
        self.claimsTable.setFocusPolicy(Qt.NoFocus)
        self.claimsTable.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.claimsTable.header().setDefaultAlignment(Qt.AlignCenter)
        self.claimsTable.setSortingEnabled(True)
        self.claimsTable.doubleClicked.connect(self.open_game)

        # Create the Claims Model
        self.claimsTableModel = QStandardItemModel()
        labels = ["#","Timestamp","Type","Board","Players","Move"]
        self.claimsTableModel.setHorizontalHeaderLabels(labels)
        self.claimsTable.setModel(self.claimsTableModel)

        # Create the Scan & Stop Button Box
        self.buttonBox = ButtonBox(self)

        # Create the Sources Button
        sourcesButton = QPushButton("Add Sources")
        sourcesButton.setObjectName("sources")
        sourcesButton.clicked.connect(self.slots.on_sourcesButton_clicked)

        # Create the Status Bar
        self.pixmapCheck = QPixmap(resource_path("check_icon.png"))
        self.pixmapError = QPixmap(resource_path("error_icon.png"))

        self.sourceLabel = QLabel()
        self.sourceLabel.setObjectName("source-label")
        self.sourceImage = QLabel()
        self.sourceImage.setObjectName("source-image")
        self.downloadLabel = QLabel()
        self.downloadLabel.setObjectName("download-label")
        self.downloadImage = QLabel()
        self.downloadImage.setObjectName("download-image")
        self.scanningLabel = QLabel()
        self.scanningLabel.setObjectName("scanning")
        self.spinnerLabel = QLabel()
        self.spinnerLabel.setVisible(False)
        self.spinnerLabel.setObjectName("spinner")

        self.spinner = QMovie(resource_path("spinner.gif"))
        self.spinner.setScaledSize(QSize(self.iconsSize, self.iconsSize))
        self.spinnerLabel.setMovie(self.spinner)
        self.spinner.start()

        self.statusBar = QStatusBar()
        self.statusBar.setSizeGripEnabled(False)

        self.statusBar.addWidget(self.sourceLabel)
        self.statusBar.addWidget(self.sourceImage)
        self.statusBar.addWidget(self.downloadLabel)
        self.statusBar.addWidget(self.downloadImage)
        self.statusBar.addWidget(self.scanningLabel)
        self.statusBar.addWidget(self.spinnerLabel)
        self.statusBar.addPermanentWidget(sourcesButton)
        self.statusBar.setContentsMargins(10,5,9,5)

        # Container Layout for the Central Widget
        containerLayout = QVBoxLayout()
        containerLayout.setSpacing(0)
        containerLayout.addWidget(self.claimsTable)
        containerLayout.addWidget(self.buttonBox)

        # Central Widget
        containerWidget = QWidget()
        containerWidget.setLayout(containerLayout)

        self.setCentralWidget(containerWidget)
        self.setStatusBar(self.statusBar)

    def open_game(self):
        """ TODO: Double click should open a window to replay the game."""
        pass

    def resize_claimsTable(self):
        """ Resize the table (if needed) after the insertion of a new element"""
        for index in range(0,6):
            self.claimsTable.resizeColumnToContents(index)

    def set_slots(self, slots):
        """ Connect the Slots """
        self.slots = slots

    def add_to_table(self,type,bo_number,players,move):
        """ Add new row to the claimsTable
        Args:
            type: The type of the draw (3 Fold Repetition, 5 Fold Repetition,
                                        50 Moves Rule, 75 Moves Rule).
            bo_number: The number of the boards, if this information is available.
            players: The name of the players.
            move: With which move the draw is valid.
        """

        # Before insertion, remove rows as descripted in the remove_rows function
        self.remove_rows(type,players)

        timestamp = str(datetime.now().strftime('%H:%M:%S'))
        row = []
        items = [str(self.rowCount+1),timestamp,type,bo_number,players,move]

        """ Convert each item(str) to QStandardItem, make the necessary stylistic
        additions and append it to row."""

        for index in range(len(items)):
            standardItem = QStandardItem(items[index])
            standardItem.setTextAlignment(Qt.AlignCenter)

            if(index == 2):
                font = standardItem.font()
                font.setBold(True)
                standardItem.setFont(font)

            if (items[index] == "5 Fold Repetition" or items[index] == "75 Moves Rule"):
                standardItem.setData(QColor(255,0,0), Qt.ForegroundRole)

            row.append(standardItem)

        self.claimsTableModel.appendRow(row)
        self.rowCount = self.rowCount+1

        # After the insertion resize the table
        self.resize_claimsTable()

        # Always the last row(the bottom of the table) should be visible.
        self.claimsTable.scrollToBottom()

        #Send Notification
        self.notify(type,players,move)

    def notify(self,type,players,move):
        """ Send notification depending on the OS.
        Args:
            type: The type of the draw (3 Fold Repetition, 5 Fold Repetition,
                                        50 Moves Rule, 75 Moves Rule).
            players: The names of the players.
            move: With which move the draw is valid.
        """
        if (platform.system() == "Darwin"):
            self.mac_notification.clearNotifications()
            self.mac_notification.notify(type,players,move)
        elif(platform.system() == "Windows"):
                self.win_notification.show_toast(type,
                                   players+"\n"+move,
                                   icon_path=resource_path("logo.ico"),
                                   duration=5,
                                   threaded=True)

    def remove_from_table(self,index):
        """ Remove element from the claimsTable.
        Args:
            index: The index of the row we want to remove. First row has index=0.
        """
        self.claimsTableModel.removeRow(index)

    def remove_rows(self,type,players):
        """ Removes a existing row from the Claims Table when same players made
        the same type of draw with a new move - or they made 5 Fold Repetition
        over the 3 Fold or 75 Moves Rule over 50 moves Rule.

        Args:
            type: The type of the draw (3 Fold Repetition, 5 Fold Repetition,
                                        50 Moves Rule, 75 Moves Rule).
            players: The names of the players.
        """
        for index in range(self.rowCount):

            try:
                modelType = self.claimsTableModel.item(index,2).text()
                modelPlayers = self.claimsTableModel.item(index,4).text()
            except AttributeError:
                modelType = ""
                modelPlayers = ""

            if (modelType == type and modelPlayers == players):
                self.remove_from_table(index)
                self.rowCount = self.rowCount - 1
                break
            elif (type == "5 Fold Repetition" and modelType == "3 Fold Repetition" and modelPlayers == players) :
                self.remove_from_table(index)
                self.rowCount = self.rowCount - 1
                break
            elif (type == "75 Moves Rule" and modelType == "50 Moves Rule" and modelPlayers == players):
                self.remove_from_table(index)
                self.rowCount = self.rowCount - 1
                break

    def clear_table(self):
        """ Clear all the elements off the Claims Table and resets the rowCount. """
        for index in range(self.rowCount):
            self.claimsTableModel.removeRow(0)
        self.rowCount = 0

    def set_sources_status(self,status,validSources=None):
        """ Adds the sourcess in the statusBar.
        Args:
            status(str): The status of the validity of the sources.
                "ok": At least one source is valid.
                "error": None of the sources are valid.
            validSources(list): The list of valid sources, if there is any.
                This list is used here to display the ToolTip.
        """
        self.sourceLabel.setText("Sources:")

        # Set the ToolTip if there are sources.
        try:
            text = ""
            for index in range(len(validSources)):
                if (index == len(validSources) - 1):
                    number = str(index+1)
                    text = text+number+") "+validSources[index].get_value()
                else:
                    number = str(index+1)
                    text = text+number+") "+validSources[index].get_value()+"\n"
            self.sourceLabel.setToolTip(text)
        except TypeError:
            pass

        if (status == "ok"):
            self.sourceImage.setPixmap(self.pixmapCheck.scaled(self.iconsSize,self.iconsSize,transformMode=Qt.SmoothTransformation))
        else:
            self.sourceImage.setPixmap(self.pixmapError.scaled(self.iconsSize,self.iconsSize,transformMode=Qt.SmoothTransformation))

    def set_download_status(self,status):
        """ Adds download status in the statusBar.
        Args:
            status(str): The status of the download(s).
                "ok": The download of the sources is successful.
                "error": The download of the sources failed.
                "stop": The download process stopped.
        """
        timestamp = str(datetime.now().strftime('%H:%M:%S'))
        self.downloadLabel.setText(timestamp+" Download:")
        if (status == "ok"):
            self.downloadImage.setPixmap(self.pixmapCheck.scaled(self.iconsSize,self.iconsSize,transformMode=Qt.SmoothTransformation))
        elif (status == "error"):
            self.downloadImage.setPixmap(self.pixmapError.scaled(self.iconsSize,self.iconsSize,transformMode=Qt.SmoothTransformation))
        elif (status == "stop"):
            self.downloadImage.clear()
            self.downloadLabel.clear()

    def set_scan_status(self,status):
        """ Adds the scan status in the statusBar.
        Args:
            status(str): The status of the scan process.
                "active": The scan process is active.
                "error": The scan process waits for a new file.
                "stop": The scan process stopped.
        """
        if (status == "wait"):
            self.scanningLabel.setText("Scan: Waiting")
            self.spinnerLabel.setVisible(False)
        elif (status == "active"):
            self.scanningLabel.setText("Scanning...")
            self.spinnerLabel.setVisible(True)
        elif (status == "stop"):
            self.scanningLabel.clear()
            self.spinnerLabel.setVisible(False)

    def change_scanButton_text(self,status):
        """ Changes the text of the scanButton depending on the status of the application.
        Args:
            status(str): The status of the scan process.
                "active": The scan process is active.
                "wait": The scan process is being terminated
                "stop": The scan process stopped.
        """
        if (status == "active"):
            self.buttonBox.scanButton.setText("Scanning PGN...")
        elif (status == "stop"):
            self.buttonBox.scanButton.setText("Start Scan")
        elif(status == "wait"):
            self.buttonBox.scanButton.setText("Please Wait")

    def enable_buttons(self):
        self.buttonBox.scanButton.setEnabled(True)
        self.buttonBox.stopButton.setEnabled(True)

    def disable_buttons(self):
        self.buttonBox.scanButton.setEnabled(False)
        self.buttonBox.stopButton.setEnabled(False)

    def enable_statusBar(self):
        """ Show download and scan status messages - if they were previously
        hidden (by disable_statusBar) - from the statusBar."""
        self.downloadLabel.setVisible(True)
        self.scanningLabel.setVisible(True)
        self.downloadImage.setVisible(True)

    def disable_statusBar(self):
        """ Hide download and scan status messages from the statusBar. """
        self.downloadLabel.setVisible(False)
        self.downloadImage.setVisible(False)
        self.scanningLabel.setVisible(False)
        self.spinnerLabel.setVisible(False)

    def closeEvent(self,event):
        """ Reimplement the close button
        If the program is actively scanning a pgn a warning dialog shall be raised
        in order to make sure that the user didn't clicked the close Button accidentally.
        Args:
            event: The exit QEvent.
        """
        try:
            if (self.slots.scanWorker.isRunning):
                exitDialog = QMessageBox()
                exitDialog.setWindowTitle("Warning")
                exitDialog.setText("Scanning in Progress")
                exitDialog.setInformativeText("Do you want to quit?")
                exitDialog.setIcon(exitDialog.Warning)
                exitDialog.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
                exitDialog.setDefaultButton(QMessageBox.Cancel)
                replay = exitDialog.exec()

                if replay == QMessageBox.Yes:
                    event.accept()
                else:
                    event.ignore()
        except:
            event.accept()

    def load_warning(self):
        """ Displays a Warning Dialog.
        trigger:
            User clicked the "Start Scanning" Button without any valid pgn source.
        """
        warningDialog = QMessageBox()
        warningDialog.setIcon(warningDialog.Warning)
        warningDialog.setWindowTitle("Warning")
        warningDialog.setText("PGN File(s) Not Found")
        warningDialog.setInformativeText("Please enter at least one valid PGN source.")
        warningDialog.exec()

    def load_about_dialog(self):
        """ Displays the About Dialog."""
        self.aboutDialog = AboutDialog()
        self.aboutDialog.set_GUI()
        self.aboutDialog.show()
예제 #32
0
class DirectoriesDialog(QMainWindow):
    def __init__(self, app, **kwargs):
        super().__init__(None, **kwargs)
        self.app = app
        self.lastAddedFolder = platform.INITIAL_FOLDER_IN_DIALOGS
        self.recentFolders = Recent(self.app, 'recentFolders')
        self._setupUi()
        self._updateScanTypeList()
        self.directoriesModel = DirectoriesModel(self.app.model.directory_tree, view=self.treeView)
        self.directoriesDelegate = DirectoriesDelegate()
        self.treeView.setItemDelegate(self.directoriesDelegate)
        self._setupColumns()
        self.app.recentResults.addMenu(self.menuLoadRecent)
        self.app.recentResults.addMenu(self.menuRecentResults)
        self.recentFolders.addMenu(self.menuRecentFolders)
        self._updateAddButton()
        self._updateRemoveButton()
        self._updateLoadResultsButton()
        self._updateActionsState()
        self._setupBindings()

    def _setupBindings(self):
        self.appModeRadioBox.itemSelected.connect(self.appModeButtonSelected)
        self.showPreferencesButton.clicked.connect(self.app.actionPreferences.trigger)
        self.scanButton.clicked.connect(self.scanButtonClicked)
        self.loadResultsButton.clicked.connect(self.actionLoadResults.trigger)
        self.addFolderButton.clicked.connect(self.actionAddFolder.trigger)
        self.removeFolderButton.clicked.connect(self.removeFolderButtonClicked)
        self.treeView.selectionModel().selectionChanged.connect(self.selectionChanged)
        self.app.recentResults.itemsChanged.connect(self._updateLoadResultsButton)
        self.recentFolders.itemsChanged.connect(self._updateAddButton)
        self.recentFolders.mustOpenItem.connect(self.app.model.add_directory)
        self.directoriesModel.foldersAdded.connect(self.directoriesModelAddedFolders)
        self.app.willSavePrefs.connect(self.appWillSavePrefs)

    def _setupActions(self):
        # (name, shortcut, icon, desc, func)
        ACTIONS = [
            ('actionLoadResults', 'Ctrl+L', '', tr("Load Results..."), self.loadResultsTriggered),
            ('actionShowResultsWindow', '', '', tr("Results Window"), self.app.showResultsWindow),
            ('actionAddFolder', '', '', tr("Add Folder..."), self.addFolderTriggered),
        ]
        createActions(ACTIONS, self)

    def _setupMenu(self):
        self.menubar = QMenuBar(self)
        self.menubar.setGeometry(QRect(0, 0, 42, 22))
        self.menuFile = QMenu(self.menubar)
        self.menuFile.setTitle(tr("File"))
        self.menuView = QMenu(self.menubar)
        self.menuView.setTitle(tr("View"))
        self.menuHelp = QMenu(self.menubar)
        self.menuHelp.setTitle(tr("Help"))
        self.menuLoadRecent = QMenu(self.menuFile)
        self.menuLoadRecent.setTitle(tr("Load Recent Results"))
        self.setMenuBar(self.menubar)

        self.menuFile.addAction(self.actionLoadResults)
        self.menuFile.addAction(self.menuLoadRecent.menuAction())
        self.menuFile.addSeparator()
        self.menuFile.addAction(self.app.actionClearPictureCache)
        self.menuFile.addSeparator()
        self.menuFile.addAction(self.app.actionQuit)
        self.menuView.addAction(self.app.actionPreferences)
        self.menuView.addAction(self.actionShowResultsWindow)
        self.menuView.addAction(self.app.actionIgnoreList)
        self.menuHelp.addAction(self.app.actionShowHelp)
        self.menuHelp.addAction(self.app.actionOpenDebugLog)
        self.menuHelp.addAction(self.app.actionAbout)

        self.menubar.addAction(self.menuFile.menuAction())
        self.menubar.addAction(self.menuView.menuAction())
        self.menubar.addAction(self.menuHelp.menuAction())

        # Recent folders menu
        self.menuRecentFolders = QMenu()
        self.menuRecentFolders.addAction(self.actionAddFolder)
        self.menuRecentFolders.addSeparator()

        # Recent results menu
        self.menuRecentResults = QMenu()
        self.menuRecentResults.addAction(self.actionLoadResults)
        self.menuRecentResults.addSeparator()

    def _setupUi(self):
        self.setWindowTitle(self.app.NAME)
        self.resize(420, 338)
        self.centralwidget = QWidget(self)
        self.verticalLayout = QVBoxLayout(self.centralwidget)
        hl = QHBoxLayout()
        label = QLabel(tr("Application Mode:"), self)
        label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        hl.addWidget(label)
        self.appModeRadioBox = RadioBox(
            self,
            items=[tr("Standard"), tr("Music"), tr("Picture")],
            spread=False
        )
        hl.addWidget(self.appModeRadioBox)
        self.verticalLayout.addLayout(hl)
        hl = QHBoxLayout()
        hl.setAlignment(Qt.AlignLeft)
        label = QLabel(tr("Scan Type:"), self)
        label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        hl.addWidget(label)
        self.scanTypeComboBox = QComboBox(self)
        self.scanTypeComboBox.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
        self.scanTypeComboBox.setMaximumWidth(400)
        hl.addWidget(self.scanTypeComboBox)
        self.showPreferencesButton = QPushButton(tr("More Options"), self.centralwidget)
        self.showPreferencesButton.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        hl.addWidget(self.showPreferencesButton)
        self.verticalLayout.addLayout(hl)
        self.promptLabel = QLabel(tr("Select folders to scan and press \"Scan\"."), self.centralwidget)
        self.verticalLayout.addWidget(self.promptLabel)
        self.treeView = QTreeView(self.centralwidget)
        self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.treeView.setAcceptDrops(True)
        triggers = QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed\
            | QAbstractItemView.SelectedClicked
        self.treeView.setEditTriggers(triggers)
        self.treeView.setDragDropOverwriteMode(True)
        self.treeView.setDragDropMode(QAbstractItemView.DropOnly)
        self.treeView.setUniformRowHeights(True)
        self.verticalLayout.addWidget(self.treeView)
        self.horizontalLayout = QHBoxLayout()
        self.removeFolderButton = QPushButton(self.centralwidget)
        self.removeFolderButton.setIcon(QIcon(QPixmap(":/minus")))
        self.removeFolderButton.setShortcut("Del")
        self.horizontalLayout.addWidget(self.removeFolderButton)
        self.addFolderButton = QPushButton(self.centralwidget)
        self.addFolderButton.setIcon(QIcon(QPixmap(":/plus")))
        self.horizontalLayout.addWidget(self.addFolderButton)
        spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem1)
        self.loadResultsButton = QPushButton(self.centralwidget)
        self.loadResultsButton.setText(tr("Load Results"))
        self.horizontalLayout.addWidget(self.loadResultsButton)
        self.scanButton = QPushButton(self.centralwidget)
        self.scanButton.setText(tr("Scan"))
        self.scanButton.setDefault(True)
        self.horizontalLayout.addWidget(self.scanButton)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.setCentralWidget(self.centralwidget)

        self._setupActions()
        self._setupMenu()

        if self.app.prefs.directoriesWindowRect is not None:
            self.setGeometry(self.app.prefs.directoriesWindowRect)
        else:
            moveToScreenCenter(self)

    def _setupColumns(self):
        header = self.treeView.header()
        header.setStretchLastSection(False)
        header.setSectionResizeMode(0, QHeaderView.Stretch)
        header.setSectionResizeMode(1, QHeaderView.Fixed)
        header.resizeSection(1, 100)

    def _updateActionsState(self):
        self.actionShowResultsWindow.setEnabled(self.app.resultWindow is not None)

    def _updateAddButton(self):
        if self.recentFolders.isEmpty():
            self.addFolderButton.setMenu(None)
        else:
            self.addFolderButton.setMenu(self.menuRecentFolders)

    def _updateRemoveButton(self):
        indexes = self.treeView.selectedIndexes()
        if not indexes:
            self.removeFolderButton.setEnabled(False)
            return
        self.removeFolderButton.setEnabled(True)

    def _updateLoadResultsButton(self):
        if self.app.recentResults.isEmpty():
            self.loadResultsButton.setMenu(None)
        else:
            self.loadResultsButton.setMenu(self.menuRecentResults)

    def _updateScanTypeList(self):
        try:
            self.scanTypeComboBox.currentIndexChanged[int].disconnect(self.scanTypeChanged)
        except TypeError:
            # Not connected, ignore
            pass
        self.scanTypeComboBox.clear()
        scan_options = self.app.model.SCANNER_CLASS.get_scan_options()
        for scan_option in scan_options:
            self.scanTypeComboBox.addItem(scan_option.label)
        SCAN_TYPE_ORDER = [so.scan_type for so in scan_options]
        selected_scan_type = self.app.prefs.get_scan_type(self.app.model.app_mode)
        scan_type_index = SCAN_TYPE_ORDER.index(selected_scan_type)
        self.scanTypeComboBox.setCurrentIndex(scan_type_index)
        self.scanTypeComboBox.currentIndexChanged[int].connect(self.scanTypeChanged)
        self.app._update_options()

    #--- QWidget overrides
    def closeEvent(self, event):
        event.accept()
        if self.app.model.results.is_modified:
            title = tr("Unsaved results")
            msg = tr("You have unsaved results, do you really want to quit?")
            if not self.app.confirm(title, msg):
                event.ignore()
        if event.isAccepted():
            QApplication.quit()

    #--- Events
    def addFolderTriggered(self):
        title = tr("Select a folder to add to the scanning list")
        flags = QFileDialog.ShowDirsOnly
        dirpath = str(QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags))
        if not dirpath:
            return
        self.lastAddedFolder = dirpath
        self.app.model.add_directory(dirpath)
        self.recentFolders.insertItem(dirpath)

    def appModeButtonSelected(self, index):
        if index == 2:
            mode = AppMode.Picture
        elif index == 1:
            mode = AppMode.Music
        else:
            mode = AppMode.Standard
        self.app.model.app_mode = mode
        self._updateScanTypeList()

    def appWillSavePrefs(self):
        self.app.prefs.directoriesWindowRect = self.geometry()

    def directoriesModelAddedFolders(self, folders):
        for folder in folders:
            self.recentFolders.insertItem(folder)

    def loadResultsTriggered(self):
        title = tr("Select a results file to load")
        files = ';;'.join([tr("dupeGuru Results (*.dupeguru)"), tr("All Files (*.*)")])
        destination = QFileDialog.getOpenFileName(self, title, '', files)
        if destination:
            self.app.model.load_from(destination)
            self.app.recentResults.insertItem(destination)

    def removeFolderButtonClicked(self):
        self.directoriesModel.model.remove_selected()

    def scanButtonClicked(self):
        if self.app.model.results.is_modified:
            title = tr("Start a new scan")
            msg = tr("You have unsaved results, do you really want to continue?")
            if not self.app.confirm(title, msg):
                return
        self.app.model.start_scanning()

    def scanTypeChanged(self, index):
        scan_options = self.app.model.SCANNER_CLASS.get_scan_options()
        self.app.prefs.set_scan_type(self.app.model.app_mode, scan_options[index].scan_type)
        self.app._update_options()

    def selectionChanged(self, selected, deselected):
        self._updateRemoveButton()
예제 #33
0
class MainWindow(QtWidgets.QMainWindow):

    Folder = 1
    File = 2
    Table = 3

    LogInfo = 101
    LogWarning = 102
    LogError = 103


    def LoadTableData(self,data,model):
        
        try:
            value = json.loads(data)
            table = value['table']
            model.setColumnCount(len(table))

            data = value['data']
            model.setRowCount(len(data) + 2)

            for v in table:
                model.setHeaderData(v[0],Qt.Horizontal,v[1])
                model.setData(model.index(0,v[0]),v[2])
                model.setData(model.index(1,v[0]),v[3])

            for i in range(0,len(data)):
                v = data[i]
                for j in range(0,len(v)):
                    model.setData(model.index(i+2,j),v[j])
                
            model.activeColumn = value['activeColumn']
        except Exception as e:
            pass

    def AddTreeItem(self,parent,text,type,isexpand = True):

        if parent == None:
            texts = text.split('.')
            if len(texts) > 1:
                rootItem = self.rootItem
                for i in range(0,len(texts)-1):
                    t = texts[i]
                    childItem = None
                    for j in range(0,rootItem.rowCount()):
                        childItem = rootItem.child(j,0)
                        if t == childItem.data():
                            break

                    rootItem = childItem
                parent = rootItem
                text = texts[-1]
            else:
                parent = self.rootItem

        lastFolderItem = None
        for i in range(0,parent.rowCount()):
            childItem = self.model.itemFromIndex(self.model.index(i,0,parent.index()))
            if childItem.data() == MainWindow.Folder:
                lastFolderItem = childItem

            if text == childItem.text():
                return None

        icon = None
        if type == MainWindow.Folder:
            icon = self.iconProvider.icon(QFileIconProvider.Folder)
        elif type == MainWindow.File:
            icon = self.iconProvider.icon(QFileIconProvider.File)
        elif type == MainWindow.Table:
            icon = self.iconProvider.icon(QFileIconProvider.Desktop)

        item = QStandardItem(parent)
        
        item.setIcon(icon)
        item.setText(text)
        item.setData(type)
        item.setFlags(QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsSelectable)

        if type == MainWindow.Folder and lastFolderItem != None:
            parent.insertRow(lastFolderItem.row()+1,item)
        else:
            parent.appendRow(item)
        
        if isexpand == True:
            self.tree.expand(parent.index())

        return item

    def SetRootTreeItem(self,text):
        self.rootItem = QStandardItem()
        self.rootItem.setIcon(self.iconProvider.icon(QFileIconProvider.Folder))
        self.rootItem.setText(text)
        self.rootItem.setData(MainWindow.Folder)

        self.model.appendRow(self.rootItem)

        for i in range(0,self.model.columnCount()):
            colItem = self.model.itemFromIndex(self.model.index(0,i))
            colItem.setFlags(QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsSelectable)

    def GetTreeItemShortPath(self,item):
        tempItem = item
        names = []
        while True:
            names.append(tempItem.text())
            tempItem = tempItem.parent()
            if tempItem == self.rootItem:
                break

        return '.'.join(reversed(names))

    def OnTreeCustomContextMenuRequested(self,pt):
        index = self.tree.indexAt(pt);  
        if index.isValid():

            item = self.model.itemFromIndex(index)

            parent = item.parent()
            if parent != None:
                item = self.model.itemFromIndex(self.model.index(item.row(),0,parent.index()))

            def OnAddTreeItem(self,item,type):
                inputDialog = InputDialog.InputDialog(self)
                ret = inputDialog.exec_()
                inputDialog.destroy()

                if QtWidgets.QDialog.Rejected == ret:
                    return

                if len(inputDialog.GetTextValue()) == 0:
                    return

                itemTable = self.AddTreeItem(item,str(inputDialog.GetTextValue()),type)

                if MainWindow.Table == type:
                    model = GridTableView.TableViewItemModel(2,0)
                    model.setParent(self.tree)
                    itemTable.setData(model,Qt.UserRole+2)

                cursor = None
                try:
                    cursor = self.db.cursor()
                    cursor.execute('insert into datas (k, v, t) values (\'{}\', \'{}\', {})'.format(self.GetTreeItemShortPath(itemTable),"None",type))
                    self.db.commit()
                except Exception as e:
                    pass
                finally:
                    cursor.close()

            def OnAddFolder(index,self = self,item = item):
                OnAddTreeItem(self,item,MainWindow.Folder)

            def OnAddFile(index,self = self,item = item):
                OnAddTreeItem(self,item,MainWindow.File)

            def OnAddTable(index,self = self,item = item):
                OnAddTreeItem(self,item,MainWindow.Table)

            def OnRename(index,self = self,item = item):
                inputDialog = InputDialog.InputDialog(self,item.text())
                ret = inputDialog.exec_()
                inputDialog.destroy()

                if QtWidgets.QDialog.Rejected == ret:
                    return

                text = inputDialog.GetTextValue()
                if len(text) == 0:
                    return

                #old_shortpath = self.GetTreeItemShortPath(item)
                

                items = []
                oldpaths = []

                if item.data() == MainWindow.Table:
                    items.append(item)
                else:
                    def GetAllChildItems(items,item):
                        for i in range(0,item.rowCount()):
                            childItem = item.child(i,0)

                            if childItem.data() != MainWindow.Table:
                                GetAllChildItems(items,childItem)
                            else:
                                items.append(childItem)
                        items.append(item)

                    GetAllChildItems(items,item)
                for v in items:
                    oldpaths.append(self.GetTreeItemShortPath(v))

                item.setText(text)
                cursor = self.db.cursor()
                for i in range(0,len(items)):
                    v = items[i]
                    oldpath = oldpaths[i]
                    cursor.execute('update datas set k=? where k=?', (self.GetTreeItemShortPath(v),oldpath))

                    findTabIndex = False
                    for i in range(0,self.tabWidget.count()):
                        if findTabIndex == True:
                            continue

                        if oldpath == self.tabWidget.tabToolTip(i):
                            findTabIndex = True
                            self.tabWidget.setTabToolTip(i,self.GetTreeItemShortPath(v))
                            if v == item and item.data() == MainWindow.Table:
                                self.tabWidget.setTabText(i,text)  
                    
                cursor.close()      
                self.db.commit()

            def OnDelete(index,self = self,item = item):
                if item == self.rootItem:
                    return

                deleyeKeys = set()
                cursor = self.db.cursor()

                if item.data() == MainWindow.Folder or item.data() == MainWindow.File:
                    cursor.execute('select * from datas')
                    shortpath = self.GetTreeItemShortPath(item)
                    for i in range(0,self.tabWidget.count()):
                        tabText = self.tabWidget.tabToolTip(i)
                        if len(tabText) >= len(shortpath) and tabText[0:len(shortpath)] == shortpath:
                            self.tabWidget.removeTab(i)
                            #if self.OnCloseTab(i) == False:
                            #    return

                    def DeleteChildItems(cursor,item):
                        for i in range(0,item.rowCount()):
                            childItem = item.child(i,0)

                            if item.data() != MainWindow.Table:
                                cursor.execute('delete from datas where k=?', (self.GetTreeItemShortPath(childItem),))
                                DeleteChildItems(cursor,childItem)
                        cursor.execute('delete from datas where k=?', (self.GetTreeItemShortPath(item),))

                    DeleteChildItems(cursor,item)
                    self.model.removeRow(item.row(),item.parent().index())

                elif item.data() == MainWindow.Table:
                    shortpath = self.GetTreeItemShortPath(item)
                    for i in range(0,self.tabWidget.count()):
                        if self.tabWidget.tabToolTip(i) == shortpath:
                            self.tabWidget.removeTab(i)
                            #if self.OnCloseTab(i) == False:
                            #    return
                    deleyeKeys.add(shortpath)
                    self.model.removeRow(item.row(),item.parent().index())

                for v in deleyeKeys:
                    try:
                        cursor.execute('delete from datas where k=?', (v,))
                    except Exception as e:
                        pass
                cursor.close()      
                self.db.commit()
            
            action_AddDir = QtWidgets.QAction("添加目录",None,triggered=OnAddFolder)
            action_AddConfig = QtWidgets.QAction("添加文件",None,triggered=OnAddFile)
            action_AddTable = QtWidgets.QAction("添加配置表",None,triggered=OnAddTable)
            action_Rename = QtWidgets.QAction("重命名",None,triggered=OnRename)
            action_Delete = QtWidgets.QAction("删除",None,triggered=OnDelete)

            menuTree = QtWidgets.QMenu("menuTree",self.tree)
            menuTree.addAction(action_AddDir)
            menuTree.addAction(action_AddConfig)
            menuTree.addAction(action_AddTable)
            menuTree.addSeparator()
            menuTree.addAction(action_Rename)
            menuTree.addSeparator()
            menuTree.addAction(action_Delete)

            if item == self.rootItem:
                action_Rename.setDisabled(True)

            if item.data() == MainWindow.Folder:
                action_AddTable.setDisabled(True)
                if item == self.rootItem:
                    action_Delete.setDisabled(True)
            elif item.data() == MainWindow.File:
                action_AddDir.setDisabled(True)
                action_AddConfig.setDisabled(True)
            elif item.data() == MainWindow.Table:
                action_AddTable.setDisabled(True)
                action_AddDir.setDisabled(True)
                action_AddConfig.setDisabled(True)
            else:
                return

            menuTree.exec_(QtGui.QCursor.pos())
            menuTree.destroy()

    def closeEvent(self, event):
        
        count = self.tabWidget.count()
        for i in reversed(range(0,count)):
            self.OnCloseTab(i)

        event.accept()

    def OnPaste(self):
        tableView = self.tabWidget.currentWidget()
        if tableView != None:
            if tableView.IsChanged == True:
                if QMessageBox.Yes == QMessageBox.information(self,'Save','Do you save changes?',QMessageBox.Yes|QMessageBox.No|QMessageBox.Cancel,QMessageBox.Yes):
                    tableView.Save()

            fileName,fileType = QtWidgets.QFileDialog.getOpenFileName(self,'Open File','','Excel File(*.xls *.xlsx)')
            if os.path.exists(fileName) and os.path.isfile(fileName):
                tableView.Paste(fileName)

    def OnExport(self):

        dialog = ExportDialog.ExportDialog(self)
        dialog.exec_()

    def DoSave(self,tableView):
        datas = tableView.Save()
        if datas != None:
            tabIndex = self.tabWidget.indexOf(tableView)

            cursor = None
        
            try:
                cursor = self.db.cursor()

                k = self.tabWidget.tabToolTip(tabIndex)
                cursor.execute('select * from datas where k=?', (k,))
                values = cursor.fetchall()

                if len(values) > 0 and values[0][0] == k:
                    cursor.execute('update datas set v=? where k=?', (datas,k))
                else:
                    cursor.execute('insert into datas (k, v, t) values (\'{}\', \'{}\', {})', (k,datas,MainWindow.Table))
                self.db.commit()
            except Exception as e:
                pass
            finally:
                if cursor != None:
                    cursor.close()

    def OnSave(self):
        tableView = self.tabWidget.currentWidget()
        if tableView != None and tableView.IsChanged == True:
            self.DoSave(tableView)
            tabIndex = self.tabWidget.indexOf(tableView)
            self.tabWidget.tabBar().setTabTextColor(tabIndex,QColor(0,0,0))

    def OnSaveAll(self):
        for i in range(0,self.tabWidget.count()):
            tableView = self.tabWidget.widget(i)
            if tableView.IsChanged == True:
                self.DoSave(tableView)
                self.tabWidget.tabBar().setTabTextColor(i,QColor(0,0,0))


    def OnUndo(self):
        tableView = self.tabWidget.currentWidget()
        if tableView != None:
            tableView.Undo()

    def OnRedo(self):
        tableView = self.tabWidget.currentWidget()
        if tableView != None:
            tableView.Redo()

    def EnableSave(self,enable,tableView):

        if enable == True:
            self.tabWidget.tabBar().setTabTextColor(self.tabWidget.indexOf(tableView),QColor(233,21,10))
        else:
            self.tabWidget.tabBar().setTabTextColor(self.tabWidget.indexOf(tableView),QColor(0,0,0))

    def OnTreeDoubleClicked(self,index):
        if index.isValid() == False:
            return

        item = self.model.itemFromIndex(index)
        shortpath = self.GetTreeItemShortPath(item)

        findTabIndex = -1
        if item.data() == MainWindow.Table:
            for i in range(0,self.tabWidget.count()):
                if self.tabWidget.tabToolTip(i) == shortpath:
                    findTabIndex = i
                    break
            if findTabIndex != -1:
                self.tabWidget.setCurrentIndex(findTabIndex)
            else:
                tableView = GridTableView.GridTableView(item.data(Qt.UserRole+2),self.tabWidget)

                tabIndex = self.tabWidget.addTab(tableView,item.text())
                self.tabWidget.setTabToolTip(tabIndex,shortpath)
                self.tabWidget.setCurrentWidget(tableView)
            pass
        pass

    def OnCloseTab(self,tabId):
        tableView = self.tabWidget.widget(tabId)
        if tableView.IsChanged == True:
            ret = QMessageBox.information(self,'Save','Do you save changes?',QMessageBox.Yes|QMessageBox.No|QMessageBox.Cancel,QMessageBox.Yes)
            if QMessageBox.Yes == ret:
                self.DoSave(tableView)
            elif QMessageBox.Cancel == ret:
                return False

        self.tabWidget.removeTab(tabId)
        return True

    @property
    def Settings(self):

        if self.setting == None:
            self.setting = {}
            try:
                with open("Settings.cfg",'r') as f:
                    self.setting = json.load(f)
            except IOError as e:
                pass

        return self.setting

    def GetTreeModel(self):
        return self.model

    def __del__(self):
        print('MainWindow.__del__')

        if self.db!= None:
            self.db.close()

        try:
            with open("Settings.cfg",'w') as f:
                json.dump(self.setting,f)
        except IOError as e:
            pass
        finally:
            pass
        pass


    def __init__(self): 
        super(MainWindow, self).__init__()
        
        uic.loadUi('MainWindow.ui', self)

        self.db = None
        self.rootItem = None
        self.setting = None
        self.fileSystemWatcher = QFileSystemWatcher()

        self.oldWindowTitle = self.windowTitle()
        
        self.iconProvider = QFileIconProvider()

        splitterH = QSplitter(self.centralwidget)
        self.verticalLayout.addWidget(splitterH)

        self.tree = QTreeView(splitterH)

        self.model = QStandardItemModel(self.tree)
        self.model.setHorizontalHeaderLabels(['Name'])
        self.model.setColumnCount(1)

        self.tree.setModel(self.model)

        selectionModel = QItemSelectionModel(self.model)
        self.tree.setSelectionModel(selectionModel)
        self.tree.setUniformRowHeights(True)
        self.tree.header().setStretchLastSection(False)
        self.tree.viewport().setAttribute(Qt.WA_StaticContents)
        self.tree.setAttribute(Qt.WA_MacShowFocusRect, False)

        self.tree.header().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.tree.header().setStretchLastSection(False);

        self.tree.setHeaderHidden(True)

        self.tree.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tree.customContextMenuRequested['QPoint'].connect(self.OnTreeCustomContextMenuRequested)
        self.tree.doubleClicked.connect(self.OnTreeDoubleClicked)

        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(1)
        self.tree.setSizePolicy(sizePolicy)

        splitterH.addWidget(self.tree)

        self.setStatusBar(None)

        self.tabWidget = QTabWidget(splitterH)
        self.tabWidget.setTabsClosable(True)

        self.tabWidget.resize(self.tabWidget.size().width(),self.size().height()/3*1)

        self.tabWidget.tabCloseRequested['int'].connect(self.OnCloseTab)
        

        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(7)
        self.tabWidget.setSizePolicy(sizePolicy)

        splitterH.addWidget(self.tabWidget)
       
        self.action_Save.setShortcut(Qt.CTRL|Qt.Key_S)
        self.action_Save.triggered.connect(self.OnSave)

        self.actionParse_Excel.triggered.connect(self.OnPaste)
        #self.action_Export_Code.triggered.connect(self.OnExportData)

        self.actionUndo.setShortcut(Qt.CTRL|Qt.Key_Z)
        self.actionUndo.triggered.connect(self.OnUndo)

        self.actionRedo.setShortcut(Qt.CTRL|Qt.Key_Y)
        self.actionRedo.triggered.connect(self.OnRedo)

        self.SetRootTreeItem('root')

        #self.currentZip = ''
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.DelayStart)
        self.timer.start(100)

    def GetAllDatasFromDB(self):
        cursor = self.db.cursor()
        cursor.execute('select * from datas order by k asc')
        values = cursor.fetchall()
        cursor.close()
        return values

    @property
    def TreeRootItem(self):
        return self.rootItem

    def DelayStart(self):
        self.timer.stop()
        self.timer = None
   
        sf = SelectFolder.SelectFolder(self)
        if sf.exec_() == QtWidgets.QDialog.Rejected:
            self.close()

        sf.destroy()
        currentPath = sf.GetFolder()

        if os.path.exists(currentPath) == False:
            return

        self.setWindowTitle(self.oldWindowTitle + ' - ' + currentPath)

        self.db = sqlite3.connect(currentPath)
        values = self.GetAllDatasFromDB()
        for k,v,t in values:
            item = self.AddTreeItem(None,k, t,False)

            if t == MainWindow.Table:  
                model = GridTableView.TableViewItemModel(2,0)
                model.setParent(self.tree)
                if v != 'None':
                    self.LoadTableData(v,model)

                item.setData(model,Qt.UserRole+2)

        self.tree.expand(self.rootItem.index())



        
예제 #34
0
class DirectoriesDialog(QMainWindow):
    def __init__(self, app, **kwargs):
        super().__init__(None, **kwargs)
        self.app = app
        self.lastAddedFolder = platform.INITIAL_FOLDER_IN_DIALOGS
        self.recentFolders = Recent(self.app, 'recentFolders')
        self._setupUi()
        self.directoriesModel = DirectoriesModel(self.app.model.directory_tree,
                                                 view=self.treeView)
        self.directoriesDelegate = DirectoriesDelegate()
        self.treeView.setItemDelegate(self.directoriesDelegate)
        self._setupColumns()
        self.app.recentResults.addMenu(self.menuLoadRecent)
        self.app.recentResults.addMenu(self.menuRecentResults)
        self.recentFolders.addMenu(self.menuRecentFolders)
        self._updateAddButton()
        self._updateRemoveButton()
        self._updateLoadResultsButton()
        self._setupBindings()

    def _setupBindings(self):
        self.scanButton.clicked.connect(self.scanButtonClicked)
        self.loadResultsButton.clicked.connect(self.actionLoadResults.trigger)
        self.addFolderButton.clicked.connect(self.actionAddFolder.trigger)
        self.removeFolderButton.clicked.connect(self.removeFolderButtonClicked)
        self.treeView.selectionModel().selectionChanged.connect(
            self.selectionChanged)
        self.app.recentResults.itemsChanged.connect(
            self._updateLoadResultsButton)
        self.recentFolders.itemsChanged.connect(self._updateAddButton)
        self.recentFolders.mustOpenItem.connect(self.app.model.add_directory)
        self.directoriesModel.foldersAdded.connect(
            self.directoriesModelAddedFolders)
        self.app.willSavePrefs.connect(self.appWillSavePrefs)

    def _setupActions(self):
        # (name, shortcut, icon, desc, func)
        ACTIONS = [
            ('actionLoadResults', 'Ctrl+L', '', tr("Load Results..."),
             self.loadResultsTriggered),
            ('actionShowResultsWindow', '', '', tr("Results Window"),
             self.app.showResultsWindow),
            ('actionAddFolder', '', '', tr("Add Folder..."),
             self.addFolderTriggered),
        ]
        createActions(ACTIONS, self)

    def _setupMenu(self):
        self.menubar = QMenuBar(self)
        self.menubar.setGeometry(QRect(0, 0, 42, 22))
        self.menuFile = QMenu(self.menubar)
        self.menuFile.setTitle(tr("File"))
        self.menuView = QMenu(self.menubar)
        self.menuView.setTitle(tr("View"))
        self.menuHelp = QMenu(self.menubar)
        self.menuHelp.setTitle(tr("Help"))
        self.menuLoadRecent = QMenu(self.menuFile)
        self.menuLoadRecent.setTitle(tr("Load Recent Results"))
        self.setMenuBar(self.menubar)

        self.menuFile.addAction(self.actionLoadResults)
        self.menuFile.addAction(self.menuLoadRecent.menuAction())
        self.menuFile.addSeparator()
        self.menuFile.addAction(self.app.actionQuit)
        self.menuView.addAction(self.app.actionPreferences)
        self.menuView.addAction(self.actionShowResultsWindow)
        self.menuView.addAction(self.app.actionIgnoreList)
        self.menuHelp.addAction(self.app.actionShowHelp)
        self.menuHelp.addAction(self.app.actionOpenDebugLog)
        self.menuHelp.addAction(self.app.actionAbout)

        self.menubar.addAction(self.menuFile.menuAction())
        self.menubar.addAction(self.menuView.menuAction())
        self.menubar.addAction(self.menuHelp.menuAction())

        # Recent folders menu
        self.menuRecentFolders = QMenu()
        self.menuRecentFolders.addAction(self.actionAddFolder)
        self.menuRecentFolders.addSeparator()

        # Recent results menu
        self.menuRecentResults = QMenu()
        self.menuRecentResults.addAction(self.actionLoadResults)
        self.menuRecentResults.addSeparator()

    def _setupUi(self):
        self.setWindowTitle(self.app.NAME)
        self.resize(420, 338)
        self.centralwidget = QWidget(self)
        self.verticalLayout = QVBoxLayout(self.centralwidget)
        self.promptLabel = QLabel(
            tr("Select folders to scan and press \"Scan\"."),
            self.centralwidget)
        self.verticalLayout.addWidget(self.promptLabel)
        self.treeView = QTreeView(self.centralwidget)
        self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.treeView.setAcceptDrops(True)
        triggers = QAbstractItemView.DoubleClicked|QAbstractItemView.EditKeyPressed\
            |QAbstractItemView.SelectedClicked
        self.treeView.setEditTriggers(triggers)
        self.treeView.setDragDropOverwriteMode(True)
        self.treeView.setDragDropMode(QAbstractItemView.DropOnly)
        self.treeView.setUniformRowHeights(True)
        self.verticalLayout.addWidget(self.treeView)
        self.horizontalLayout = QHBoxLayout()
        self.removeFolderButton = QPushButton(self.centralwidget)
        self.removeFolderButton.setIcon(QIcon(QPixmap(":/minus")))
        self.removeFolderButton.setShortcut("Del")
        self.horizontalLayout.addWidget(self.removeFolderButton)
        self.addFolderButton = QPushButton(self.centralwidget)
        self.addFolderButton.setIcon(QIcon(QPixmap(":/plus")))
        self.horizontalLayout.addWidget(self.addFolderButton)
        spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding,
                                  QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem1)
        self.loadResultsButton = QPushButton(self.centralwidget)
        self.loadResultsButton.setText(tr("Load Results"))
        self.horizontalLayout.addWidget(self.loadResultsButton)
        self.scanButton = QPushButton(self.centralwidget)
        self.scanButton.setText(tr("Scan"))
        self.scanButton.setDefault(True)
        self.horizontalLayout.addWidget(self.scanButton)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.setCentralWidget(self.centralwidget)

        self._setupActions()
        self._setupMenu()

        if self.app.prefs.directoriesWindowRect is not None:
            self.setGeometry(self.app.prefs.directoriesWindowRect)
        else:
            moveToScreenCenter(self)

    def _setupColumns(self):
        header = self.treeView.header()
        header.setStretchLastSection(False)
        header.setSectionResizeMode(0, QHeaderView.Stretch)
        header.setSectionResizeMode(1, QHeaderView.Fixed)
        header.resizeSection(1, 100)

    def _updateAddButton(self):
        if self.recentFolders.isEmpty():
            self.addFolderButton.setMenu(None)
        else:
            self.addFolderButton.setMenu(self.menuRecentFolders)

    def _updateRemoveButton(self):
        indexes = self.treeView.selectedIndexes()
        if not indexes:
            self.removeFolderButton.setEnabled(False)
            return
        self.removeFolderButton.setEnabled(True)

    def _updateLoadResultsButton(self):
        if self.app.recentResults.isEmpty():
            self.loadResultsButton.setMenu(None)
        else:
            self.loadResultsButton.setMenu(self.menuRecentResults)

    #--- QWidget overrides
    def closeEvent(self, event):
        event.accept()
        if self.app.model.results.is_modified:
            title = tr("Unsaved results")
            msg = tr("You have unsaved results, do you really want to quit?")
            if not self.app.confirm(title, msg):
                event.ignore()
        if event.isAccepted():
            QApplication.quit()

    #--- Events
    def addFolderTriggered(self):
        title = tr("Select a folder to add to the scanning list")
        flags = QFileDialog.ShowDirsOnly
        dirpath = str(
            QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder,
                                             flags))
        if not dirpath:
            return
        self.lastAddedFolder = dirpath
        self.app.model.add_directory(dirpath)
        self.recentFolders.insertItem(dirpath)

    def appWillSavePrefs(self):
        self.app.prefs.directoriesWindowRect = self.geometry()

    def directoriesModelAddedFolders(self, folders):
        for folder in folders:
            self.recentFolders.insertItem(folder)

    def loadResultsTriggered(self):
        title = tr("Select a results file to load")
        files = ';;'.join(
            [tr("dupeGuru Results (*.dupeguru)"),
             tr("All Files (*.*)")])
        destination = QFileDialog.getOpenFileName(self, title, '', files)
        if destination:
            self.app.model.load_from(destination)
            self.app.recentResults.insertItem(destination)

    def removeFolderButtonClicked(self):
        self.directoriesModel.model.remove_selected()

    def scanButtonClicked(self):
        if self.app.model.results.is_modified:
            title = tr("Start a new scan")
            msg = tr(
                "You have unsaved results, do you really want to continue?")
            if not self.app.confirm(title, msg):
                return
        self.app.model.start_scanning()

    def selectionChanged(self, selected, deselected):
        self._updateRemoveButton()
예제 #35
0
class DirectoriesDialog(QMainWindow):
    def __init__(self, app, **kwargs):
        super().__init__(None, **kwargs)
        self.app = app
        self.lastAddedFolder = platform.INITIAL_FOLDER_IN_DIALOGS
        self.recentFolders = Recent(self.app, "recentFolders")
        self._setupUi()
        self.directoriesModel = DirectoriesModel(self.app.model.directory_tree, view=self.treeView)
        self.directoriesDelegate = DirectoriesDelegate()
        self.treeView.setItemDelegate(self.directoriesDelegate)
        self._setupColumns()
        self.app.recentResults.addMenu(self.menuLoadRecent)
        self.app.recentResults.addMenu(self.menuRecentResults)
        self.recentFolders.addMenu(self.menuRecentFolders)
        self._updateAddButton()
        self._updateRemoveButton()
        self._updateLoadResultsButton()
        self._setupBindings()

    def _setupBindings(self):
        self.scanButton.clicked.connect(self.scanButtonClicked)
        self.loadResultsButton.clicked.connect(self.actionLoadResults.trigger)
        self.addFolderButton.clicked.connect(self.actionAddFolder.trigger)
        self.removeFolderButton.clicked.connect(self.removeFolderButtonClicked)
        self.treeView.selectionModel().selectionChanged.connect(self.selectionChanged)
        self.app.recentResults.itemsChanged.connect(self._updateLoadResultsButton)
        self.recentFolders.itemsChanged.connect(self._updateAddButton)
        self.recentFolders.mustOpenItem.connect(self.app.model.add_directory)
        self.directoriesModel.foldersAdded.connect(self.directoriesModelAddedFolders)
        self.app.willSavePrefs.connect(self.appWillSavePrefs)

    def _setupActions(self):
        # (name, shortcut, icon, desc, func)
        ACTIONS = [
            ("actionLoadResults", "Ctrl+L", "", tr("Load Results..."), self.loadResultsTriggered),
            ("actionShowResultsWindow", "", "", tr("Results Window"), self.app.showResultsWindow),
            ("actionAddFolder", "", "", tr("Add Folder..."), self.addFolderTriggered),
        ]
        createActions(ACTIONS, self)

    def _setupMenu(self):
        self.menubar = QMenuBar(self)
        self.menubar.setGeometry(QRect(0, 0, 42, 22))
        self.menuFile = QMenu(self.menubar)
        self.menuFile.setTitle(tr("File"))
        self.menuView = QMenu(self.menubar)
        self.menuView.setTitle(tr("View"))
        self.menuHelp = QMenu(self.menubar)
        self.menuHelp.setTitle(tr("Help"))
        self.menuLoadRecent = QMenu(self.menuFile)
        self.menuLoadRecent.setTitle(tr("Load Recent Results"))
        self.setMenuBar(self.menubar)

        self.menuFile.addAction(self.actionLoadResults)
        self.menuFile.addAction(self.menuLoadRecent.menuAction())
        self.menuFile.addSeparator()
        self.menuFile.addAction(self.app.actionQuit)
        self.menuView.addAction(self.app.actionPreferences)
        self.menuView.addAction(self.actionShowResultsWindow)
        self.menuView.addAction(self.app.actionIgnoreList)
        self.menuHelp.addAction(self.app.actionShowHelp)
        self.menuHelp.addAction(self.app.actionOpenDebugLog)
        self.menuHelp.addAction(self.app.actionAbout)

        self.menubar.addAction(self.menuFile.menuAction())
        self.menubar.addAction(self.menuView.menuAction())
        self.menubar.addAction(self.menuHelp.menuAction())

        # Recent folders menu
        self.menuRecentFolders = QMenu()
        self.menuRecentFolders.addAction(self.actionAddFolder)
        self.menuRecentFolders.addSeparator()

        # Recent results menu
        self.menuRecentResults = QMenu()
        self.menuRecentResults.addAction(self.actionLoadResults)
        self.menuRecentResults.addSeparator()

    def _setupUi(self):
        self.setWindowTitle(self.app.NAME)
        self.resize(420, 338)
        self.centralwidget = QWidget(self)
        self.verticalLayout = QVBoxLayout(self.centralwidget)
        self.promptLabel = QLabel(tr('Select folders to scan and press "Scan".'), self.centralwidget)
        self.verticalLayout.addWidget(self.promptLabel)
        self.treeView = QTreeView(self.centralwidget)
        self.treeView.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.treeView.setAcceptDrops(True)
        triggers = (
            QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed | QAbstractItemView.SelectedClicked
        )
        self.treeView.setEditTriggers(triggers)
        self.treeView.setDragDropOverwriteMode(True)
        self.treeView.setDragDropMode(QAbstractItemView.DropOnly)
        self.treeView.setUniformRowHeights(True)
        self.verticalLayout.addWidget(self.treeView)
        self.horizontalLayout = QHBoxLayout()
        self.removeFolderButton = QPushButton(self.centralwidget)
        self.removeFolderButton.setIcon(QIcon(QPixmap(":/minus")))
        self.removeFolderButton.setShortcut("Del")
        self.horizontalLayout.addWidget(self.removeFolderButton)
        self.addFolderButton = QPushButton(self.centralwidget)
        self.addFolderButton.setIcon(QIcon(QPixmap(":/plus")))
        self.horizontalLayout.addWidget(self.addFolderButton)
        spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem1)
        self.loadResultsButton = QPushButton(self.centralwidget)
        self.loadResultsButton.setText(tr("Load Results"))
        self.horizontalLayout.addWidget(self.loadResultsButton)
        self.scanButton = QPushButton(self.centralwidget)
        self.scanButton.setText(tr("Scan"))
        self.scanButton.setDefault(True)
        self.horizontalLayout.addWidget(self.scanButton)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.setCentralWidget(self.centralwidget)

        self._setupActions()
        self._setupMenu()

        if self.app.prefs.directoriesWindowRect is not None:
            self.setGeometry(self.app.prefs.directoriesWindowRect)
        else:
            moveToScreenCenter(self)

    def _setupColumns(self):
        header = self.treeView.header()
        header.setStretchLastSection(False)
        header.setSectionResizeMode(0, QHeaderView.Stretch)
        header.setSectionResizeMode(1, QHeaderView.Fixed)
        header.resizeSection(1, 100)

    def _updateAddButton(self):
        if self.recentFolders.isEmpty():
            self.addFolderButton.setMenu(None)
        else:
            self.addFolderButton.setMenu(self.menuRecentFolders)

    def _updateRemoveButton(self):
        indexes = self.treeView.selectedIndexes()
        if not indexes:
            self.removeFolderButton.setEnabled(False)
            return
        self.removeFolderButton.setEnabled(True)

    def _updateLoadResultsButton(self):
        if self.app.recentResults.isEmpty():
            self.loadResultsButton.setMenu(None)
        else:
            self.loadResultsButton.setMenu(self.menuRecentResults)

    # --- QWidget overrides
    def closeEvent(self, event):
        event.accept()
        if self.app.model.results.is_modified:
            title = tr("Unsaved results")
            msg = tr("You have unsaved results, do you really want to quit?")
            if not self.app.confirm(title, msg):
                event.ignore()
        if event.isAccepted():
            QApplication.quit()

    # --- Events
    def addFolderTriggered(self):
        title = tr("Select a folder to add to the scanning list")
        flags = QFileDialog.ShowDirsOnly
        dirpath = str(QFileDialog.getExistingDirectory(self, title, self.lastAddedFolder, flags))
        if not dirpath:
            return
        self.lastAddedFolder = dirpath
        self.app.model.add_directory(dirpath)
        self.recentFolders.insertItem(dirpath)

    def appWillSavePrefs(self):
        self.app.prefs.directoriesWindowRect = self.geometry()

    def directoriesModelAddedFolders(self, folders):
        for folder in folders:
            self.recentFolders.insertItem(folder)

    def loadResultsTriggered(self):
        title = tr("Select a results file to load")
        files = ";;".join([tr("dupeGuru Results (*.dupeguru)"), tr("All Files (*.*)")])
        destination = QFileDialog.getOpenFileName(self, title, "", files)
        if destination:
            self.app.model.load_from(destination)
            self.app.recentResults.insertItem(destination)

    def removeFolderButtonClicked(self):
        self.directoriesModel.model.remove_selected()

    def scanButtonClicked(self):
        if self.app.model.results.is_modified:
            title = tr("Start a new scan")
            msg = tr("You have unsaved results, do you really want to continue?")
            if not self.app.confirm(title, msg):
                return
        self.app.model.start_scanning()

    def selectionChanged(self, selected, deselected):
        self._updateRemoveButton()
예제 #36
0
class AddBookmarkDialog(QDialog, Ui_AddBookmarkDialog):
    """
    Class implementing a dialog to add a bookmark or a bookmark folder.
    """
    def __init__(self, parent=None, bookmarksManager=None):
        """
        Constructor
        
        @param parent reference to the parent widget (QWidget)
        @param bookmarksManager reference to the bookmarks manager
            object (BookmarksManager)
        """
        super(AddBookmarkDialog, self).__init__(parent)
        self.setupUi(self)

        self.__bookmarksManager = bookmarksManager
        self.__addedNode = None
        self.__addFolder = False

        if self.__bookmarksManager is None:
            import Helpviewer.HelpWindow
            self.__bookmarksManager = \
                Helpviewer.HelpWindow.HelpWindow.bookmarksManager()

        self.__proxyModel = AddBookmarkProxyModel(self)
        model = self.__bookmarksManager.bookmarksModel()
        self.__proxyModel.setSourceModel(model)

        self.__treeView = QTreeView(self)
        self.__treeView.setModel(self.__proxyModel)
        self.__treeView.expandAll()
        self.__treeView.header().setStretchLastSection(True)
        self.__treeView.header().hide()
        self.__treeView.setItemsExpandable(False)
        self.__treeView.setRootIsDecorated(False)
        self.__treeView.setIndentation(10)
        self.__treeView.show()

        self.locationCombo.setModel(self.__proxyModel)
        self.locationCombo.setView(self.__treeView)

        self.addressEdit.setInactiveText(self.tr("Url"))
        self.nameEdit.setInactiveText(self.tr("Title"))

        self.resize(self.sizeHint())

    def setUrl(self, url):
        """
        Public slot to set the URL of the new bookmark.
        
        @param url URL of the bookmark (string)
        """
        self.addressEdit.setText(url)
        self.resize(self.sizeHint())

    def url(self):
        """
        Public method to get the URL of the bookmark.
        
        @return URL of the bookmark (string)
        """
        return self.addressEdit.text()

    def setTitle(self, title):
        """
        Public method to set the title of the new bookmark.
        
        @param title title of the bookmark (string)
        """
        self.nameEdit.setText(title)

    def title(self):
        """
        Public method to get the title of the bookmark.
        
        @return title of the bookmark (string)
        """
        return self.nameEdit.text()

    def setDescription(self, description):
        """
        Public method to set the description of the new bookmark.
        
        @param description description of the bookamrk (string)
        """
        self.descriptionEdit.setPlainText(description)

    def description(self):
        """
        Public method to get the description of the bookmark.
        
        @return description of the bookamrk (string)
        """
        return self.descriptionEdit.toPlainText()

    def setCurrentIndex(self, idx):
        """
        Public method to set the current index.
        
        @param idx current index to be set (QModelIndex)
        """
        proxyIndex = self.__proxyModel.mapFromSource(idx)
        self.__treeView.setCurrentIndex(proxyIndex)
        self.locationCombo.setCurrentIndex(proxyIndex.row())

    def currentIndex(self):
        """
        Public method to get the current index.
        
        @return current index (QModelIndex)
        """
        idx = self.locationCombo.view().currentIndex()
        idx = self.__proxyModel.mapToSource(idx)
        return idx

    def setFolder(self, folder):
        """
        Public method to set the dialog to "Add Folder" mode.
        
        @param folder flag indicating "Add Folder" mode (boolean)
        """
        self.__addFolder = folder

        if folder:
            self.setWindowTitle(self.tr("Add Folder"))
            self.addressEdit.setVisible(False)
        else:
            self.setWindowTitle(self.tr("Add Bookmark"))
            self.addressEdit.setVisible(True)

        self.resize(self.sizeHint())

    def isFolder(self):
        """
        Public method to test, if the dialog is in "Add Folder" mode.
        
        @return flag indicating "Add Folder" mode (boolean)
        """
        return self.__addFolder

    def addedNode(self):
        """
        Public method to get a reference to the added bookmark node.
        
        @return reference to the added bookmark node (BookmarkNode)
        """
        return self.__addedNode

    def accept(self):
        """
        Public slot handling the acceptance of the dialog.
        """
        if (not self.__addFolder and not self.addressEdit.text()) or \
           not self.nameEdit.text():
            super(AddBookmarkDialog, self).accept()
            return

        from .BookmarkNode import BookmarkNode

        idx = self.currentIndex()
        if not idx.isValid():
            idx = self.__bookmarksManager.bookmarksModel().index(0, 0)
        parent = self.__bookmarksManager.bookmarksModel().node(idx)

        if self.__addFolder:
            type_ = BookmarkNode.Folder
        else:
            type_ = BookmarkNode.Bookmark
        bookmark = BookmarkNode(type_)
        bookmark.title = self.nameEdit.text()
        if not self.__addFolder:
            bookmark.url = self.addressEdit.text()
        bookmark.desc = self.descriptionEdit.toPlainText()

        self.__bookmarksManager.addBookmark(parent, bookmark)
        self.__addedNode = bookmark

        super(AddBookmarkDialog, self).accept()
예제 #37
0
class ShellTableEditor(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.model = None
        self.shell_model = ShellTableTreeModel()
        self.tree_view = QTreeView(self)
        self.tree_view.setModel(self.shell_model)
        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.init_navigation(), 0)
        layout.addWidget(self.tree_view, 1)
        layout.addWidget(self.init_docs(), 0)

    def get_icon(self, name):
        return self.style().standardIcon(name)

    def init_navigation(self):
        self.prev_button = QPushButton(self.get_icon(QStyle.SP_ArrowLeft), "")
        self.prev_button.setFlat(True)
        self.next_button = QPushButton(self.get_icon(QStyle.SP_ArrowRight), "")
        self.next_button.setFlat(True)
        self.prev_button.clicked.connect(self.handle_prev_button_clicked)
        self.next_button.clicked.connect(self.handle_next_button_clicked)
        self.current_index = QSpinBox()
        self.current_index.setMinimumWidth(60)
        self.current_index.valueChanged.connect(self.handle_current_index_changed)
        box = QWidget(self)
        box_layout = QHBoxLayout(box)
        box_layout.setContentsMargins(0, 0, 0, 0)
        box.setLayout(box_layout)
        box_layout.addWidget(self.prev_button, 0)
        box_layout.addWidget(self.next_button, 0)
        box_layout.addWidget(QLabel("Index:"), 0)
        box_layout.addWidget(self.current_index, 0)
        box_layout.addStretch(1)
        return box

    def init_docs(self):
        box = QWidget(self)
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        box.setLayout(layout)
        layout.addWidget(QLabel(DOC_CAPACITY), 0, Qt.AlignTop)
        layout.addWidget(QLabel(DOC_RECOIL), 0, Qt.AlignTop)
        layout.addWidget(QLabel(DOC_RELOAD), 1, Qt.AlignTop)
        return box

    def handle_current_index_changed(self, value):
        parent_index = QModelIndex()
        qindex = self.shell_model.index(self.current_index.value(), 0, parent_index)
        self.tree_view.setRootIndex(qindex)
        self.tree_view.expandAll()

    def handle_prev_button_clicked(self):
        self.current_index.setValue(self.current_index.value() - 1)

    def handle_next_button_clicked(self):
        self.current_index.setValue(self.current_index.value() + 1)

    def set_model(self, model):
        self.model = model
        if model is None:
            self.shell_model.update([])
            self.current_index.setMaximum(0)
        else:
            self.shell_model.update(self.model.data.entries)
            self.current_index.setMaximum(len(self.model.data.entries) - 1)
            self.handle_current_index_changed(0)
            self.tree_view.header().resizeSection(0, 120)