Exemple #1
0
class MVCConfigurationGUI(MVCConfigurationBase):
    """
    GUI implementation of MVCConfigurationBase
    """
    def __init__(self, configuration):
        super().__init__(configuration)
        assertMainThread()
        srv = Services.getService("MainWindow")
        srv.aboutToClose.connect(self._aboutToClose)
        confMenu = srv.menuBar().addMenu("&Configuration")
        toolBar = srv.getToolBar()

        configuration.configNameChanged.connect(self._configNameChanged)
        configuration.dirtyChanged.connect(self._dirtyChanged)

        style = QApplication.style()
        self.actLoad = QAction(
            QIcon.fromTheme("document-open",
                            style.standardIcon(QStyle.SP_DialogOpenButton)),
            "Open config", self)
        self.actLoad.triggered.connect(self._execLoad)
        self.actSave = QAction(
            QIcon.fromTheme("document-save",
                            style.standardIcon(QStyle.SP_DialogSaveButton)),
            "Save config", self)
        self.actSave.triggered.connect(self._execSaveConfig)
        self.actSaveWithGuiState = QAction(
            QIcon.fromTheme("document-save",
                            style.standardIcon(QStyle.SP_DialogSaveButton)),
            "Save config sync gui state", self)
        self.actSaveWithGuiState.triggered.connect(
            self._execSaveConfigWithGuiState)
        self.actNew = QAction(
            QIcon.fromTheme("document-new",
                            style.standardIcon(QStyle.SP_FileIcon)),
            "New config", self)
        self.actNew.triggered.connect(self._execNew)

        self.actActivate = QAction(
            QIcon.fromTheme("arrow-up", style.standardIcon(QStyle.SP_ArrowUp)),
            "Initialize", self)
        self.actActivate.triggered.connect(self.activate)
        self.actDeactivate = QAction(
            QIcon.fromTheme("arrow-down",
                            style.standardIcon(QStyle.SP_ArrowDown)),
            "Deinitialize", self)
        self.actDeactivate.triggered.connect(self.deactivate)

        confMenu.addAction(self.actLoad)
        confMenu.addAction(self.actSave)
        confMenu.addAction(self.actSaveWithGuiState)
        confMenu.addAction(self.actNew)
        confMenu.addAction(self.actActivate)
        confMenu.addAction(self.actDeactivate)
        toolBar.addAction(self.actLoad)
        toolBar.addAction(self.actSave)
        toolBar.addAction(self.actNew)
        toolBar.addAction(self.actActivate)
        toolBar.addAction(self.actDeactivate)

        self.recentConfigs = [QAction() for i in range(10)]
        self.recentConfigs[0].setShortcut(QKeySequence(Qt.CTRL + Qt.Key_R))
        confMenu.addSeparator()
        recentMenu = confMenu.addMenu("Recent")
        for a in self.recentConfigs:
            a.setVisible(False)
            a.triggered.connect(self._openRecent)
            recentMenu.addAction(a)

        self.mainWidget = srv.newDockWidget("Configuration", None,
                                            Qt.LeftDockWidgetArea)
        self.treeView = QTreeView(self.mainWidget)
        self.treeView.setHeaderHidden(False)
        self.treeView.setSelectionMode(QAbstractItemView.NoSelection)
        self.treeView.setEditTriggers(self.treeView.EditKeyPressed
                                      | self.treeView.AnyKeyPressed)
        self.treeView.setAllColumnsShowFocus(True)
        self.treeView.setExpandsOnDoubleClick(False)
        self.treeView.setDragEnabled(True)
        self.treeView.setDropIndicatorShown(True)
        self.treeView.setDragDropMode(QAbstractItemView.DragOnly)
        self.mainWidget.setWidget(self.treeView)
        self.treeView.setModel(self.model)
        self.treeView.header().setStretchLastSection(True)
        self.treeView.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.treeView.doubleClicked.connect(self._onItemDoubleClicked)
        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.treeView.customContextMenuRequested.connect(
            self._execTreeViewContextMenu)
        # expand applications by default
        self.treeView.setExpanded(self.model.index(1, 0), True)
        self.delegate = PropertyDelegate(self.model, ITEM_ROLE,
                                         ConfigurationModel.PropertyContent,
                                         self)
        self.treeView.setItemDelegate(self.delegate)

        self.restoreState()
        srv.aboutToClose.connect(self.saveState)
        # a list of dock widgets displaying subgraphs
        self._graphViews = []
        # make sure that the graph views are closed when the config is closed
        self._configuration.subConfigRemoved.connect(self._subConfigRemoved)
        self._waitForActivated = None
        self._waitForOpenState = None

    def _execLoad(self):
        assertMainThread()
        if self._checkDirty():
            return
        fn, _ = QFileDialog.getOpenFileName(self.mainWidget,
                                            "Load configuration",
                                            self.cfgfile,
                                            filter="*.json")
        if fn is not None and fn != "":
            logger.debug("Loading config file %s", fn)
            try:
                self.loadConfig(fn)
            except Exception as e:  # pylint: disable=broad-except
                logger.exception("Error while loading configuration %s: %s",
                                 fn, str(e))
                QMessageBox.warning(self.mainWidget,
                                    "Error while loading configuration",
                                    str(e))

    def _openRecent(self):
        """
        Called when the user clicks on a recent config.

        :return:
        """
        if self._checkDirty():
            return
        action = self.sender()
        fn = action.data()
        try:
            self.loadConfig(fn)
        except Exception as e:  # pylint: disable=broad-except
            # catching general exception is wanted here.
            logger.exception("Error while loading configuration %s: %s", fn,
                             str(e))
            QMessageBox.warning(self.mainWidget,
                                "Error while loading configuration", str(e))

    def _checkDirty(self):
        if self._configuration.dirty():
            ans = QMessageBox.question(
                None,
                "Save changes?",
                "There are unsaved changes. Do you want to save them?",
                buttons=QMessageBox.Save | QMessageBox.Discard
                | QMessageBox.Cancel,
                defaultButton=QMessageBox.Save)
            if ans == QMessageBox.Save:
                self.saveConfig()
                return False
            if ans == QMessageBox.Cancel:
                return True
        return False

    def _aboutToClose(self, mainWindow):
        if self._checkDirty():
            mainWindow.ignoreCloseEvent()

    def _execNew(self):
        assertMainThread()
        if self._checkDirty():
            return
        fn, _ = QFileDialog.getSaveFileName(self.mainWidget,
                                            "New configuration",
                                            ".",
                                            filter="*.json")
        if fn is not None and fn != "":
            logger.debug("Creating config file %s", fn)
            self.newConfig(fn)

    def _execSaveConfig(self):
        if self.configuration().filename() is None:
            self._execSaveConfigAs()
        else:
            self.saveConfig()

    def _execSaveConfigWithGuiState(self):
        if self.configuration().filename() is None:
            self._execSaveConfigAs()
        else:
            self.saveConfigWithGuiState()

    def _execSaveConfigAs(self):
        """
        Opens a file dialog to get the save file name and calls saveConfig.

        :return:
        """
        assertMainThread()
        fn, _ = QFileDialog.getSaveFileName(self.mainWidget,
                                            "Save configuration as", ".",
                                            "*.json")
        if fn is not None and fn != "":
            self.saveConfigAs(fn)

    def _addGraphView(self, subConfig):
        g = subConfig.getGraph()
        # remove already deleted graph views from internal list
        valid_graphViews = []
        for gv in self._graphViews:
            if shiboken2.isValid(gv):  # pylint: disable=no-member
                valid_graphViews.append(gv)
        self._graphViews = valid_graphViews
        # check if graph view is already there
        for gv in self._graphViews:
            if gv.widget().scene().graph == g:
                logger.info("Graph view already exists.")
                return
        # create new graph view
        srv = Services.getService("MainWindow")
        graphDw = srv.newDockWidget("Graph (%s)" % (subConfig.getName()),
                                    parent=None,
                                    defaultArea=Qt.RightDockWidgetArea,
                                    allowedArea=Qt.RightDockWidgetArea
                                    | Qt.BottomDockWidgetArea)
        graphDw.setAttribute(Qt.WA_DeleteOnClose, True)
        assert isinstance(graphDw, QDockWidget)
        graphView = GraphEditorView(graphDw)
        graphView.setScene(GraphScene(subConfig.getGraph(), graphDw))
        graphDw.setWidget(graphView)
        self._graphViews.append(graphDw)
        graphDw.visibleChanged.connect(self._removeGraphViewFromList)

    def _subConfigRemoved(self, subConfigName, configType):
        g = self._configuration.subConfigByNameAndTye(subConfigName,
                                                      configType).getGraph()
        for gv in self._graphViews:
            if gv.widget().scene().graph == g:
                logger.debug("deleting graph view for subconfig %s",
                             subConfigName)
                gv.deleteLater()

    def _removeGraphViewFromList(self, visible):
        if visible:
            return
        gv = self.sender()
        try:
            self._graphViews.remove(gv)
            logger.debug("removed graphview from list")
        except ValueError:
            logger.debug("graphview not in list, ignored")

    def _execTreeViewContextMenu(self, point):
        index = self.treeView.indexAt(point)
        item = self.model.data(index, ITEM_ROLE)
        if isinstance(item, ConfigurationModel.SubConfigContent):
            m = QMenu()
            a1 = QAction("Edit graph")
            m.addAction(a1)
            a1.triggered.connect(lambda: self._addGraphView(item.subConfig))
            if self.model.isApplication(index):
                a2 = QAction("Select Application")
                a2.triggered.connect(lambda: self.changeActiveApp(
                    self.model.data(index, Qt.DisplayRole)))
                a3 = QAction("Init Application")
                a3.triggered.connect(lambda: self._changeActiveAppAndInit(
                    self.model.data(index, Qt.DisplayRole)))
                m.addActions([a2, a3])
                pbsrv = Services.getService("PlaybackControl")
                m2 = m.addMenu("Init and load sequence")
                m3 = m.addMenu("Init, load and play")
                s1 = []
                s2 = []
                for a in pbsrv.recentSeqs:
                    assert isinstance(a, QAction)
                    if a.isVisible():
                        # pylint: disable=cell-var-from-loop
                        # the below statements are tested and work
                        aseq = QAction(a.text())
                        aseq.triggered.connect(lambda arg1=a.data(
                        ), seq=a.data(): self._changeActiveAppInitAndLoad(
                            self.model.data(index, Qt.DisplayRole), seq, False)
                                               )
                        s1.append(aseq)
                        aseq = QAction(a.text())
                        aseq.triggered.connect(lambda arg1=a.data(
                        ), seq=a.data(): self._changeActiveAppInitAndLoad(
                            self.model.data(index, Qt.DisplayRole), seq, True))
                        # pylint: enable=cell-var-from-loop
                        s2.append(aseq)
                m2.addActions(s1)
                m3.addActions(s2)
            m.exec_(self.treeView.mapToGlobal(point))
            return
        if self.model.isSubConfigParent(
                index) == Configuration.CONFIG_TYPE_APPLICATION:
            m = QMenu()
            a = QAction("Add application")
            m.addAction(a)
            a = m.exec_(self.treeView.mapToGlobal(point))
            if a is not None:
                self._configuration.addNewApplication()
            return
        if self.model.isSubConfigParent(
                index) == Configuration.CONFIG_TYPE_COMPOSITE:
            m = QMenu()
            a = QAction("Add composite filter")
            m.addAction(a)
            a = m.exec_(self.treeView.mapToGlobal(point))
            if a is not None:
                self._configuration.addNewCompositeFilter()
            return

    def _configNameChanged(self, cfgfile):
        logger.debug("_configNameChanged: %s", cfgfile)
        assertMainThread()
        self.cfgfile = cfgfile
        self._dirtyChanged(self._configuration.dirty())
        foundIdx = None
        for i, a in enumerate(self.recentConfigs):
            if a.data() == cfgfile:
                foundIdx = i
        if foundIdx is None:
            foundIdx = len(self.recentConfigs) - 1
        for i in range(foundIdx, 0, -1):
            self.recentConfigs[i].setText(self.recentConfigs[i - 1].text())
            self.recentConfigs[i].setData(self.recentConfigs[i - 1].data())
            self.recentConfigs[i].setVisible(
                self.recentConfigs[i - 1].data() is not None)
        self.recentConfigs[0].setText(cfgfile)
        self.recentConfigs[0].setData(cfgfile)
        self.recentConfigs[0].setVisible(True)

    def _dirtyChanged(self, dirty):
        srv = Services.getService("MainWindow")
        if self.cfgfile is None:
            title = "nexxT: <unnamed>"
        else:
            title = "nexxT: " + self.cfgfile
        if dirty:
            title += " *"
        srv.setWindowTitle(title)

    def _onItemDoubleClicked(self, index):
        assertMainThread()
        if self.model.isApplication(index):
            app = self.model.data(index, Qt.DisplayRole)
            self.changeActiveApp(app)
        else:
            self.treeView.edit(index)

    def _changeActiveAppAndInit(self, app):
        """
        Call this slot to activate and init an application

        :param app: can be either an Application instance or the name of an application
        :return:
        """
        assertMainThread()
        if isinstance(app, str):
            app = self.configuration().applicationByName(app)
        currentApp = Application.activeApplication
        if currentApp is not None:
            currentApp = currentApp.getApplication()
        self._waitForActivated = app
        self.changeActiveApp(app.getName())

    def _changeActiveAppInitAndLoad(self, app, sequence, startPlay):
        self._waitForOpenState = (app, sequence, startPlay)
        self._changeActiveAppAndInit(app)

    def appActivated(self, name, app):  # pylint: disable=unused-argument
        """
        Called when the application is activated.

        :param name: the application name
        :param app: An ActiveApplication instance.
        :return:
        """
        assertMainThread()
        if app is not None:
            self.activeAppStateChange(app.getState())
            app.stateChanged.connect(self.activeAppStateChange)
            if self._waitForActivated == app.getApplication():
                MethodInvoker(self.activate, Qt.QueuedConnection)
        else:
            self.actActivate.setEnabled(False)
            self.actDeactivate.setEnabled(False)
        self._waitForActivated = None

    def _disconnectSingleShotPlay(self):
        assertMainThread()
        pbsrv = Services.getService("PlaybackControl")
        try:
            pbsrv.playbackPaused.disconnect(self._singleShotPlay)
        except RuntimeError:
            # we are already disconnected.
            pass

    def _singleShotPlay(self):
        assertMainThread()
        pbsrv = Services.getService("PlaybackControl")
        MethodInvoker(pbsrv.startPlayback, Qt.QueuedConnection)
        self._disconnectSingleShotPlay()

    def activeAppStateChange(self, newState):
        """
        Called when the active application changes its state.

        :param newState: the new application's state (see FilterState)
        :return:
        """
        assertMainThread()
        if newState == FilterState.CONSTRUCTED:
            self.actActivate.setEnabled(True)
        else:
            self.actActivate.setEnabled(False)
        if newState == FilterState.ACTIVE:
            if self._waitForOpenState is not None:
                app, pbfile, startPlay = self._waitForOpenState
                self._waitForOpenState = None
                if app == Application.activeApplication.getApplication(
                ).getName():
                    pbsrv = Services.getService("PlaybackControl")
                    if startPlay:
                        pbsrv.playbackPaused.connect(self._singleShotPlay)
                        QTimer.singleShot(2000, self._disconnectSingleShotPlay)
                    MethodInvoker(pbsrv.browser.setActive, Qt.QueuedConnection,
                                  pbfile)
            self.actDeactivate.setEnabled(True)
            self.actSaveWithGuiState.setEnabled(False)
        else:
            self.actDeactivate.setEnabled(False)
            self.actSaveWithGuiState.setEnabled(True)

    def restoreState(self):
        """
        Restore the state of the configuration gui service (namely the recently
        open config files). This is saved in QSettings because it is used
        across config files.

        :return:
        """
        logger.debug("restoring config state ...")
        settings = QSettings()
        v = settings.value("ConfigurationRecentFiles")
        if v is not None and isinstance(v, QByteArray):
            ds = QDataStream(v)
            recentFiles = ds.readQStringList()
            idx = 0
            for f in recentFiles:
                if f != "" and f is not None:
                    self.recentConfigs[idx].setData(f)
                    self.recentConfigs[idx].setText(f)
                    self.recentConfigs[idx].setVisible(True)
                    idx += 1
                    if idx >= len(self.recentConfigs):
                        break
        logger.debug("restoring config state done")

    def saveState(self):
        """
        Save the state of the configuration gui service (namely the recently
        open config files). This is saved in QSettings because it is used
        across config files.

        :return:
        """
        logger.debug("saving config state ...")
        settings = QSettings()
        b = QByteArray()
        ds = QDataStream(b, QIODevice.WriteOnly)
        l = [
            rc.data() for rc in self.recentConfigs
            if rc.isVisible() and rc.data() is not None and rc.data() != ""
        ]
        ds.writeQStringList(l)
        settings.setValue("ConfigurationRecentFiles", b)
        logger.debug("saving config state done (%s)", l)
Exemple #2
0
class BindiffViewerDialog(QDialog):
	def __init__(self, bv, match_db, role, primary_be, secondary_be):
		super(BindiffViewerDialog, self).__init__()

		self.bv = bv
		self.primary_be = primary_be
		self.secondary_be = secondary_be
		self.role = role

		# UI
		self.match_model = BindiffMatchModel(bv, match_db, role, primary_be, secondary_be)

		self.match_view = QTreeView()
		self.match_view.setModel(self.match_model)

		self.match_view.setSelectionMode(QTreeView.ExtendedSelection)

		self.match_view.setContextMenuPolicy(Qt.CustomContextMenu)
		self.match_view.customContextMenuRequested.connect(self.match_view_context_menu_requested)
		self.match_view.doubleClicked.connect(self.match_view_double_clicked)

		self.match_view.setRootIsDecorated(False)
		self.match_view.setFont(binaryninjaui.getMonospaceFont(self))

		for i in range(len(self.match_model.column_infos)):
			self.match_view.resizeColumnToContents(i)

		self.match_view.setSortingEnabled(True)
		self.match_view.sortByColumn(0, Qt.AscendingOrder)

		layout = QVBoxLayout()
		layout.addWidget(self.match_view)

		self.setLayout(layout)
		self.setWindowTitle("BinDiff Viewer")
		self.resize(1000, 640)
		flags = self.windowFlags()
		flags |= Qt.WindowMaximizeButtonHint
		flags &= ~Qt.WindowContextHelpButtonHint
		self.setWindowFlags(flags)

	def match_view_double_clicked(self, index):
		if not index.isValid():
			assert(False)
			return
		if self.role == None:
			return

		entry = self.match_model.entries[index.row()]
		if self.role == 0:
			address = entry["address1"]
		elif self.role == 1:
			address = entry["address2"]
		else:
			assert(False)

		self.bv.navigate(self.bv.file.view, address)

	def match_view_context_menu_requested(self, pos):
		if self.role == None:
			return

		selected_indices = self.match_view.selectionModel().selectedIndexes()

		# This may return each row multiple times, so we uniquify
		selected = set([i.row() for i in selected_indices])

		def action_port_symbols():
			for i in selected:
				self.port_symbols(i)

		menu = QMenu(self.match_view)
		menu.addAction("Port symbols", action_port_symbols)
		menu.exec_(self.match_view.mapToGlobal(pos))

	def port_symbols(self, i):
		if self.role == None:
			return

		entry = self.match_model.entries[i]
		target_index = self.role
		source_index = 1 if target_index == 0 else 0

		source_name = entry["name{}".format(source_index + 1)]
		target_address = entry["address{}".format(target_index + 1)]

		old_sym = self.bv.get_symbol_at(target_address)

		target_name = None
		if old_sym:
			target_name = old_sym.name
		target_text = target_name if target_name else "<unnamed>"

		if not source_name:
			bn.log_warn("Port symbols: {} @ {:x} has no source name, skipping".format(target_text, target_address))
			return

		if old_sym and not old_sym.auto:
			bn.log_warn("Port symbols: {} @ {:x} is already named, skipping".format(target_text, target_address))
			return

		bn.log_info("Port symbols: {} @ {:x} -> {}".format(target_text, target_address, source_name))
		new_sym = bn.Symbol(bn.SymbolType.FunctionSymbol, target_address, source_name)
		self.bv.define_user_symbol(new_sym)
Exemple #3
0
class WTreeEdit(QWidget):
    """TreeEdit widget is to show and edit all of the pyleecan objects data."""

    # Signals
    dataChanged = Signal()

    def __init__(self, obj, *args, **kwargs):
        QWidget.__init__(self, *args, **kwargs)

        self.class_dict = ClassInfo().get_dict()
        self.treeDict = None  # helper to track changes
        self.obj = obj  # the object
        self.is_save_needed = False

        self.model = TreeEditModel(obj)

        self.setupUi()

        # === Signals ===
        self.selectionModel.selectionChanged.connect(self.onSelectionChanged)
        self.treeView.collapsed.connect(self.onItemCollapse)
        self.treeView.expanded.connect(self.onItemExpand)
        self.treeView.customContextMenuRequested.connect(self.openContextMenu)
        self.model.dataChanged.connect(self.onDataChanged)
        self.dataChanged.connect(self.setSaveNeeded)

        # === Finalize ===
        # set 'root' the selected item and resize columns
        self.treeView.setCurrentIndex(self.treeView.model().index(0, 0))
        self.treeView.resizeColumnToContents(0)

    def setupUi(self):
        """Setup the UI"""
        # === Widgets ===
        # TreeView
        self.treeView = QTreeView()
        # self.treeView.rootNode = model.invisibleRootItem()
        self.treeView.setModel(self.model)
        self.treeView.setAlternatingRowColors(False)

        # self.treeView.setColumnWidth(0, 150)
        self.treeView.setMinimumWidth(100)

        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.selectionModel = self.treeView.selectionModel()

        self.statusBar = QStatusBar()
        self.statusBar.setSizeGripEnabled(False)
        self.statusBar.setSizePolicy(QSizePolicy.Expanding,
                                     QSizePolicy.Maximum)
        self.statusBar.setStyleSheet(
            "QStatusBar {border: 1px solid rgb(200, 200, 200)}")
        self.saveLabel = QLabel("unsaved")
        self.saveLabel.setVisible(False)
        self.statusBar.addPermanentWidget(self.saveLabel)

        # Splitters
        self.leftSplitter = QSplitter()
        self.leftSplitter.setStretchFactor(0, 0)
        self.leftSplitter.setStretchFactor(1, 1)

        # === Layout ===
        # Horizontal Div.
        self.hLayout = QVBoxLayout()
        self.hLayout.setContentsMargins(0, 0, 0, 0)
        self.hLayout.setSpacing(0)

        # add widgets to layout
        self.hLayout.addWidget(self.leftSplitter)
        self.hLayout.addWidget(self.statusBar)

        # add widgets
        self.leftSplitter.addWidget(self.treeView)

        self.setLayout(self.hLayout)

    def update(self, obj):
        """Check if object has changed and update tree in case."""
        if not obj is self.obj:
            self.obj = obj
            self.model = TreeEditModel(obj)
            self.treeView.setModel(self.model)
            self.model.dataChanged.connect(self.onDataChanged)
            self.selectionModel = self.treeView.selectionModel()
            self.selectionModel.selectionChanged.connect(
                self.onSelectionChanged)
            self.treeView.setCurrentIndex(self.treeView.model().index(0, 0))
            self.setSaveNeeded(True)

    def setSaveNeeded(self, state=True):
        self.is_save_needed = state
        self.saveLabel.setVisible(state)

    def openContextMenu(self, point):
        """Generate and open context the menu at the given point position."""
        index = self.treeView.indexAt(point)
        pos = QtGui.QCursor.pos()

        if not index.isValid():
            return

        # get the data
        item = self.model.item(index)
        obj_info = self.model.get_obj_info(item)

        # init the menu
        menu = TreeEditContextMenu(obj_dict=obj_info, parent=self)
        menu.exec_(pos)

        self.onSelectionChanged(self.selectionModel.selection())

    def onItemCollapse(self, index):
        """Slot for item collapsed"""
        # dynamic resize
        for ii in range(3):
            self.treeView.resizeColumnToContents(ii)

    def onItemExpand(self, index):
        """Slot for item expand"""
        # dynamic resize
        for ii in range(3):
            self.treeView.resizeColumnToContents(ii)

    def onDataChanged(self, first=None, last=None):
        """Slot for changed data"""
        self.dataChanged.emit()
        self.onSelectionChanged(self.selectionModel.selection())

    def onSelectionChanged(self, itemSelection):
        """Slot for changed item selection"""
        # get the index
        if itemSelection.indexes():
            index = itemSelection.indexes()[0]
        else:
            index = self.treeView.model().index(0, 0)
            self.treeView.setCurrentIndex(index)
            return

        # get the data
        item = self.model.item(index)
        obj = item.object()
        typ = type(obj).__name__
        obj_info = self.model.get_obj_info(item)
        ref_typ = obj_info["ref_typ"] if obj_info else None

        # set statusbar information on class typ
        msg = f"{typ} (Ref: {ref_typ})" if ref_typ else f"{typ}"
        self.statusBar.showMessage(msg)

        # --- choose the respective widget by class type ---
        # numpy array -> table editor
        if typ == "ndarray":
            widget = WTableData(obj, editable=True)
            widget.dataChanged.connect(self.dataChanged.emit)

        elif typ == "MeshSolution":
            widget = WMeshSolution(obj)  # only a view (not editable)

        # list (no pyleecan type, non empty) -> table editor
        # TODO add another widget for lists of non 'primitive' types (e.g. DataND)
        elif isinstance(obj, list) and not self.isListType(ref_typ) and obj:
            widget = WTableData(obj, editable=True)
            widget.dataChanged.connect(self.dataChanged.emit)

        # generic editor
        else:
            # widget = SimpleInputWidget().generate(obj)
            widget = WTableParameterEdit(obj)
            widget.dataChanged.connect(self.dataChanged.emit)

        # show the widget
        if self.leftSplitter.widget(1) is None:
            self.leftSplitter.addWidget(widget)
        else:
            self.leftSplitter.replaceWidget(1, widget)
            widget.setParent(
                self.leftSplitter)  # workaround for PySide2 replace bug
            widget.show()
        pass

    def isListType(self, typ):
        if not typ:
            return False
        return typ[0] == "[" and typ[-1] == "]" and typ[1:-1] in self.class_dict

    def isDictType(self, typ):
        if not typ:
            return False
        return typ[0] == "{" and typ[-1] == "}" and typ[1:-1] in self.class_dict
class Ui_FE14MapEditor(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.toolbar = QToolBar()
        self.toggle_coordinate_type_action = QAction("Toggle Coordinate Type")
        self.refresh_action = QAction("Refresh")
        self.refresh_action.setShortcut(QKeySequence("Ctrl+R"))
        self.copy_spawn_action = QAction("Copy Spawn")
        self.copy_spawn_action.setShortcut(QKeySequence("Ctrl+C"))
        self.paste_spawn_action = QAction("Paste Spawn")
        self.paste_spawn_action.setShortcut(QKeySequence("Ctrl+V"))
        self.add_spawn_action = QAction("Add Spawn")
        self.delete_spawn_action = QAction("Delete Spawn")
        self.add_group_action = QAction("Add Group")
        self.delete_group_action = QAction("Delete Group")
        self.add_tile_action = QAction("Add Tile")
        self.toggle_mode_action = QAction("Toggle Mode")
        self.undo_action = QAction("Undo")
        self.undo_action.setShortcut(QKeySequence("Ctrl+Z"))
        self.redo_action = QAction("Redo")
        self.redo_action.setShortcut(QKeySequence("Ctrl+Shift+Z"))
        self.toolbar.addActions(
            [self.toggle_coordinate_type_action, self.refresh_action])
        self.toolbar.addSeparator()
        self.toolbar.addActions([
            self.copy_spawn_action, self.paste_spawn_action,
            self.add_spawn_action, self.delete_spawn_action,
            self.add_group_action, self.delete_group_action
        ])
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.add_tile_action)
        self.toolbar.addSeparator()
        self.toolbar.addAction(self.toggle_mode_action)
        self.toolbar.addSeparator()
        self.toolbar.addActions([self.undo_action, self.redo_action])
        self.addToolBar(self.toolbar)

        self.model_view = QTreeView()
        self.model_view.setHeaderHidden(True)
        self.grid = FE14MapGrid()
        self.grid_scroll = QScrollArea()
        self.grid_scroll.setWidgetResizable(True)
        self.grid_scroll.setWidget(self.grid)
        self.tile_list = QListView()
        self.terrain_pane = FE14TerrainEditorPane()
        self.spawn_pane = FE14SpawnEditorPane()

        self.model_view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.model_view_context_menu = QMenu()
        self.model_view_context_menu.addActions(
            [self.toggle_coordinate_type_action, self.refresh_action])
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addActions([
            self.copy_spawn_action, self.paste_spawn_action,
            self.add_spawn_action, self.delete_spawn_action,
            self.add_group_action, self.delete_group_action
        ])
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addAction(self.add_tile_action)
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addAction(self.toggle_mode_action)
        self.model_view_context_menu.addSeparator()
        self.model_view_context_menu.addActions(
            [self.undo_action, self.redo_action])

        self.status_bar = QStatusBar()
        self.coordinate_type_label = QLabel()
        self.status_bar.addPermanentWidget(self.coordinate_type_label)
        self.setStatusBar(self.status_bar)

        self.main_widget = QSplitter()
        self.main_widget.setOrientation(QtCore.Qt.Horizontal)
        self.main_widget.addWidget(self.model_view)
        self.main_widget.addWidget(self.grid_scroll)
        self.main_widget.addWidget(self.spawn_pane)
        self.main_widget.addWidget(self.terrain_pane)
        self.setCentralWidget(self.main_widget)
Exemple #5
0
class PocetnaStrana(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.lista_putanja = []
        self.lista_baza = []
        self.main_window = QtWidgets.QMainWindow()
        self.main_window.resize(1040, 680)
        self.main_window.setWindowTitle("Rukovalac informacionim resursima")
        icon = QtGui.QIcon("src/ikonice/logo.jpg")
        self.main_window.setWindowIcon(icon)

        self.meni_bar = MenuBar(self.main_window, parent=None)

        self.tool_bar = ToolBar(self.main_window,parent=None)
        self.tool_bar.dodaj.triggered.connect(self.dodavanje_u_datoteku)
        self.tool_bar.izmeni_tabelu.triggered.connect(self.izmena_u_datoteci)
        self.tool_bar.pretrazi.triggered.connect(self.otvori_pretragu)
        self.tool_bar.ponisti_pretragu.triggered.connect(self.ponisti_pretragu)
        self.tool_bar.ukloni_iz_tabele.triggered.connect(self.ukloni_iz_tabele)
        self.tool_bar.spoji_datoteke.triggered.connect(self.spoji_datoteke)
        # self.tool_bar.podeli_datoteku.triggered.connect(self.podeli_datoteku)
        
        status_bar = QtWidgets.QStatusBar()
        #status_bar.showMessage("Prikazan status bar!")
        self.main_window.setStatusBar(status_bar)
        
        self.central_widget = QtWidgets.QTabWidget(self.main_window) 
        self.central_widget.setTabsClosable(True) 
        self.central_widget.tabCloseRequested.connect(self.delete_tab)
        self.main_window.setCentralWidget(self.central_widget)


        self.dock = LeftDock("", parent=None)
        self.main_window.addDockWidget(Qt.LeftDockWidgetArea,self.dock) 
        self.dock.tree.setSelectionMode(QAbstractItemView.SingleSelection) 
        self.dock.tree.clicked.connect(self.read)
        self.multi_selekt = []
        
        self.listener = keyboard.Listener(on_press=self.pritisnuto_dugme, on_release=self.pusteno_dugme) 
        self.listener.start()
        self.main_window.show()

    def ponisti_pretragu(self):
        if self.central_widget.currentWidget() == None:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nijedna datoteka nije otvorena!")
            poruka.exec_()
            return

        model = self.central_widget.currentWidget().table.model()
        model = pretraga(
            [], [],
            [], 
            self.central_widget.currentWidget().meta_podaci,
            True,
            self.central_widget.currentWidget())

        self.central_widget.currentWidget().table.setModel(model)
        self.central_widget.currentWidget().table.selectRow(0)
        self.central_widget.currentWidget().column_resize()

    def podeli_datoteku(self): # kopirano od metode pretrage
        if len(self.multi_selekt) == 0:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nijedna datoteka nije selektovana za spajanje!")
            poruka.exec_()
            return
        
        prikaz = PrikazElementa(self.central_widget.currentWidget(),True)
        prikaz.exec_()
        if len(prikaz.lista_atr) != 0 and len(prikaz.lista_kriterijuma) != 0:
            model = self.central_widget.currentWidget().table.model()
            model = pretraga(
                prikaz.lista_atr, prikaz.lista_kriterijuma,
                prikaz.lista_vece_manje, 
                self.central_widget.currentWidget().meta_podaci,
                False)

            if len(model.lista_prikaz) == 0:
                poruka = QMessageBox()
                icon = QtGui.QIcon("src/ikonice/logo.jpg")
                poruka.setWindowIcon(icon)
                poruka.setWindowTitle("Upozorenje!")
                poruka.setText("Zadata pretraga nije pronasla vrednosti koje odgovaraju zadatim kriterijumima.")
                poruka.exec_()
                return

            self.central_widget.currentWidget().table.setModel(model)
        else:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Niste zadali ni jedan kriterijum za pretragu, pretraga je prekinuta.")
            poruka.exec_()
            return

    def spoji_datoteke(self):
        if len(self.multi_selekt) < 2:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nije vise datoteka selektovano za spajanje!")
            poruka.exec_()
            return

        lista = []
        self.multi_selekt.sort()
        
        brojac = 0
        while brojac < len(self.multi_selekt):
            nadjen = False
            for j in range(len(lista)):
                if self.multi_selekt[brojac].row() == lista[j]:
                    nadjen = True
                    break
            if nadjen:
                self.multi_selekt.pop(brojac)
                brojac -= 1
            lista.append(self.multi_selekt[brojac].row())
            brojac += 1

        text, ok = QtWidgets.QInputDialog.getText(None, "Spajanje datoteka", "Unesite ime nove datoteke:", QtWidgets.QLineEdit.Normal, "")
        ime_nove_datoteke = "podaci/podaci/"
        if ok:
            ime_nove_datoteke += str(text) + ".csv"
        else:
            return
            
        for i in range(0, len(self.multi_selekt)-1, 2):
            spoji_dve_sekvencijalne_datoteke(
                self.dock.model.filePath(self.multi_selekt[i]),
                self.dock.model.filePath(self.multi_selekt[i+1]),
                self.central_widget.currentWidget().meta_podaci[11].split(","),
                self.central_widget.currentWidget().meta_podaci[5].split(","),
                True,
                ime_nove_datoteke,
                self)

        self.multi_selekt = []

    def pritisnuto_dugme(self, key):
        if key == Key.ctrl_l:
            self.dock.tree.setSelectionMode(QAbstractItemView.MultiSelection) 

    def pusteno_dugme(self, key):
        if key == Key.ctrl_l:
            self.dock.tree.setSelectionMode(QAbstractItemView.SingleSelection)
            self.multi_selekt = self.dock.tree.selectedIndexes()
            

    def otvori_tabelu_levi_rodjak(self):...
    
    def otvori_tabelu_desni_rodjak(self):...

    def ukloni_iz_tabele(self):
        if self.central_widget.currentWidget() == None:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nijedna datoteka nije otvorena!")
            poruka.exec_()
            return
        
        elif not hasattr(self.central_widget.currentWidget().table, "selected_elem"):
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje")
            poruka.setText("Trenutno nijedan element nije selektovan!")
            poruka.exec_()
            return
        ime_datoteke = self.central_widget.currentWidget().meta_podaci[0]    
        tip_datoteke = self.central_widget.currentWidget().meta_podaci[1]  
        model = self.central_widget.currentWidget().table.model()
        element_selected = model.get_element(self.central_widget.currentWidget().table.selected_elem)
        putanja = self.central_widget.currentWidget().meta_podaci[4]
        lista_kljuceva = self.central_widget.currentWidget().meta_podaci[11].split(",")
        lista_atributa = self.central_widget.currentWidget().meta_podaci[5].split(",")
        lista_tip_atributa = self.central_widget.currentWidget().meta_podaci[6].split(",")
        veze = []
        veze = self.central_widget.currentWidget().meta_podaci[9].split(",")
        
        #DELETE FROM plan_studijske_grupe WHERE STU_VU_OZNAKA='IR' AND SP_OZNAKA='IT' AND SPB_BLOK=2 AND SPB_POZICIJA=2;
        if tip_datoteke == "sql":
            query= " DELETE FROM " + ime_datoteke + " WHERE "
            brojac = 0 
            for i in range(len(lista_kljuceva)):
                if brojac == 0:
                    query+= lista_kljuceva[i] + "="
                else:
                    query += " AND " + lista_kljuceva[i] + "="
                if lista_tip_atributa[i] == "str":
                    query += "'" + element_selected.__getattribute__(lista_atributa[i]) +"'"
                else:
                    query += element_selected.__getattribute__(lista_atributa[i])
                brojac +=1
            query += ";"

            try:
                self.csor.execute(query)
            except mysql.connector.errors.IntegrityError as e:
                poruka = QtWidgets.QMessageBox() 
                
                icon = QtGui.QIcon("src/ikonice/logo.jpg")
                poruka.setWindowIcon(icon)
                poruka.setWindowTitle("Upozorenje!")
                poruka.setText("Ne mozete izbrisati ovaj element!\n"+e.msg)
                poruka.exec_()
                return

            self.connection.commit()
            query = "SELECT * FROM " + ime_datoteke
            self.csor.execute(query)
            self.central_widget.currentWidget().table.model().lista_prikaz = []
            for result in self.csor.fetchall():
                lista_podataka = []
                for i in result:
                    lista_podataka.append(str(i))
                    
                self.central_widget.currentWidget().table.model().lista_prikaz.append(GenerickaKlasa(lista_atributa, lista_podataka))

        top = QModelIndex()
        top.child(0,0)
        self.central_widget.currentWidget().table.model().beginRemoveRows(top, 0, 0) 
        for i in range(len(veze)): #provjaravamo da li ima djecu, ako ima ne smije se obrisati
            if hasattr(self.central_widget.currentWidget(), "sub_table"+str(i+1)):
                if len(self.central_widget.currentWidget().__getattribute__("sub_table"+str(i+1)).model.lista_prikaz) != 0:
                    poruka = QMessageBox()
                    icon = QtGui.QIcon("src/ikonice/logo.jpg")
                    poruka.setWindowIcon(icon)
                    poruka.setWindowTitle("Upozorenje!")
                    poruka.setText("Selektovani element ne sme da se obrise zato sto se njegovi podaci koriste u podtabelama, njegovoj deci!")
                    poruka.exec_()
                    return
               
        self.central_widget.currentWidget().table.model().lista_prikaz = []
        if tip_datoteke != "sql":
            
            with open(putanja, 'r',newline='') as csvfile:
                spamreader = csv.reader(csvfile, delimiter = "\n")
                counter = 0
                prva_linija = True
                izbrisan = False
                for row in spamreader:
                    if prva_linija:
                        prva_linija = False
                        continue
                    
                    if len(row) == 0:
                        break
                    
                    objekat = GenerickaKlasa(lista_atributa, row[0].split(","))
                    nadjen = True
                    for i in range(len(lista_kljuceva)):
                        if objekat.__getattribute__(lista_kljuceva[i]) != element_selected.__getattribute__(lista_kljuceva[i]):
                            nadjen = False
                
                    if not izbrisan and nadjen:
                        izbrisan = True
                        self.central_widget.currentWidget().table.model().removeRow(counter) #uklanjamo red iz tabele
                    else:
                        self.central_widget.currentWidget().table.model().lista_prikaz.append(objekat)
                    counter += 1
            
            
            
            with open(putanja, 'w', newline='') as f:
                writer = csv.writer(f, delimiter = ",")
                writer.writerow([self.central_widget.currentWidget().putanja_meta])
                for i in range(len(self.central_widget.currentWidget().table.model().lista_prikaz)):
                    tekst = ""
                    for j in range(len(lista_atributa)):
                        tekst += str(self.central_widget.currentWidget().table.model().lista_prikaz[i].__getattribute__(lista_atributa[j]))
                        if j < len(lista_atributa)-1:
                            tekst += ","
                            
                    novi_red = tekst.split(",")
                    writer.writerow(novi_red)
        else:
            query = "SELECT * FROM " + ime_datoteke
            self.csor.execute(query)

            for result in self.csor.fetchall():
                lista_podataka = []
                for i in result:
                    lista_podataka.append(str(i))
                    
                self.central_widget.currentWidget().table.model().lista_prikaz.append(GenerickaKlasa(lista_atributa, lista_podataka))

        self.central_widget.currentWidget().table.model().endRemoveRows()

    def otvori_tabelu_roditelj(self):
        if self.central_widget.currentWidget() == None:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nijedna datoteka nije otvorena!")
            poruka.exec_()
            return
        elif not hasattr(self.central_widget.currentWidget().table, "selected_elem"):
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nijedan element nije selektovan!")
            poruka.exec_()
            return

        model = self.central_widget.currentWidget().table.model()
        element_selected = model.get_element(self.central_widget.currentWidget().table.selected_elem)
        veze = []
        veze = self.central_widget.currentWidget().meta_podaci[9].split(",")
        meta_podaci = self.central_widget.currentWidget().meta_podaci
        lista_kljuceva = []
        brojac = len(veze)-1
        lista_roditelja = []

        for i in range(len(veze)):
            if veze[brojac].find("parent_") == -1:
                veze.pop(brojac)
                brojac -= 1
            else:
                lista_roditelja.append(veze[brojac])
                brojac -= 1
        index = -1

        if len(lista_roditelja) == 0:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Selektovani element nema roditelja!")
            poruka.exec_()
            return

        elif len(lista_roditelja) > 1:
            list_tuple = ()
            for i in range(len(lista_roditelja)):
                del1 = lista_roditelja[i].find("_")+1
                del2 = lista_roditelja[i].find("(")
                ime = lista_roditelja[i][del1:del2]

                list_tuple = list_tuple + (ime,)

            input = QtWidgets.QInputDialog.getItem(
                self,
                "Izbor",
                "Trenutna tabela ima vise od 1 roditelja\nIzaberite roditelja:",
                list_tuple,
                0,
                editable=False)
                
            if input[1]:
                for i in range(len(lista_roditelja)):
                    if lista_roditelja[i].find(input[0]) != -1:
                        index = i
                        break
            else:
                return

        elif len(lista_roditelja) == 1:
            index = 0

        if index == -1:
            return
            
        del1 = lista_roditelja[index].find("_")+1
        lista_roditelja[index] = lista_roditelja[index][del1:len(lista_roditelja[index])]
        del1 = lista_roditelja[index].find("(")
        ime_roditelja = lista_roditelja[index][0:del1]
        nova_putanja = ime_roditelja[0].lower()
        
        for s in range(1, len(ime_roditelja)):
            if ime_roditelja[s].isupper():
                nova_putanja += "_" + ime_roditelja[s].lower()
            else:
                nova_putanja += ime_roditelja[s]
                
        
        nova_putanja = meta_podaci[2] + "\\" + nova_putanja
        if meta_podaci[1] == "serijska":
            nova_putanja += "_ser."
            
        elif meta_podaci[1] == "sekvencijalna":
            nova_putanja += "_sek."
            
        elif meta_podaci[1] == "sql":
            nova_putanja += "_meta_podaci_sql."

        nova_putanja += meta_podaci[3]
        
        del1 = lista_roditelja[index].find("(") + 1
        del2 = lista_roditelja[index].find(")")
        lista_kljuceva.append(lista_roditelja[index][del1:del2].split("#"))
        
        if self.central_widget.currentWidget().is_baza:
            tab = Tab(parent=self.central_widget)
            tab.pocetna_strana = self
            tab.naziv = ime_roditelja
            indeks = 0
            for i in range(len(self.imena_tabela)):
                if self.imena_tabela[i] == ime_roditelja:
                    indeks = i
                    break
            for i in range(len(self.lista_baza)):
                if self.lista_baza[i] == self.central_widget.currentWidget().indeks:
                    self.lista_baza.pop(i)
                    break
            tab.indeks = indeks
            tab.read(nova_putanja)
        else:
            tab = Tab(nova_putanja, self.central_widget)
            tab.pocetna_strana = self
            tab.read()

        indeks_roditelja = -1
        for j in range(len(tab.table.model().lista_prikaz)):
            pronadjen = True
            for m in range(len(lista_kljuceva[len(lista_kljuceva)-1])):
                kljucevi = lista_kljuceva[len(lista_kljuceva)-1][m].split("=")
                if len(kljucevi) == 1:
                    if element_selected.__getattribute__(kljucevi[0]) != tab.table.model().lista_prikaz[j].__getattribute__(kljucevi[0]):
                        pronadjen = False
                elif len(kljucevi) == 2:
                    if element_selected.__getattribute__(kljucevi[0]) != tab.table.model().lista_prikaz[j].__getattribute__(kljucevi[1]):
                        pronadjen = False
                else:
                    print("pocetna_strana.py, 124 linija, eror u len(klucevi):", len(kljucevi), "// ", kljucevi)
            if pronadjen:
                indeks_roditelja = j
                break
        
        if indeks_roditelja == -1:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Selektovani element nema roditelja sa istim kljucem kao kod selektovanog!")
            poruka.exec_()
            return

        tab.table.selectRow(indeks_roditelja)
        top = tab.table.currentIndex()
        tab.element_selected(top)

        # tab.table.setModel(tab.table.model())

        tab.btn_down.clicked.connect(self.otvori_tabelu_dete)
        tab.btn_up.clicked.connect(self.otvori_tabelu_roditelj)
        # tab.btn_left.clicked.connect(self.otvori_tabelu_levi_rodjak)
        # tab.btn_right.clicked.connect(self.otvori_tabelu_desni_rodjak)

        self.central_widget.removeTab(self.central_widget.currentIndex())
        self.central_widget.addTab(tab, ime_roditelja)

        meta = ""
        for s in range(len(self.central_widget.currentWidget().meta_podaci[0])):
            if self.central_widget.currentWidget().meta_podaci[0][s].isupper():
                meta += "_" + self.central_widget.currentWidget().meta_podaci[0][s].lower()
            else:
                meta += self.central_widget.currentWidget().meta_podaci[0][s]
                
        meta = meta[1:len(meta)]
        meta = self.central_widget.currentWidget().meta_podaci[2] + "\\" + meta
        if self.central_widget.currentWidget().meta_podaci[1] == "serijska":
            meta += "_ser."
            
        elif self.central_widget.currentWidget().meta_podaci[1] == "sekvencijalna":
            meta += "_sek."
            
        elif self.central_widget.currentWidget().meta_podaci[1] == "sql":
            meta += "_meta_podaci_sql."

        meta += self.central_widget.currentWidget().meta_podaci[3]
        
        self.lista_putanja.append(meta)
        self.lista_putanja.remove(self.lista_putanja[self.central_widget.currentIndex()])

    def otvori_tabelu_dete(self):
        if self.central_widget.currentWidget() == None:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nijedna datoteka nije otvorena!")
            poruka.exec_()
            return
        elif not hasattr(self.central_widget.currentWidget().table, "selected_elem"):
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nijedan element nije selektovan!")
            poruka.exec_()
            return
        elif self.central_widget.currentWidget().tab_widget.currentWidget() == None:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Selektovani element nema podtabele!")
            poruka.exec_()
            return
        elif len(self.central_widget.currentWidget().tab_widget.currentWidget().model.lista_prikaz) == 0:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Selektovani element nema decu u selektovanoj podtabeli!")
            poruka.exec_()
            return

        child = self.central_widget.currentWidget().tab_widget.currentWidget()
        if self.central_widget.currentWidget().is_baza:
            tab = Tab(parent=self.central_widget)
            tab.pocetna_strana = self
            tab.naziv = child.naziv
            indeks = 0
            for i in range(len(self.imena_tabela)):
                if self.imena_tabela[i] == child.naziv:
                    indeks = i
                    break
            for i in range(len(self.lista_baza)):
                if self.lista_baza[i] == self.central_widget.currentWidget().indeks:
                    self.lista_baza.pop(i)
                    break
            tab.indeks = indeks
            # self.lista_baza.append(indeks)
            tab.read(child.putanja)
        else:
            tab = Tab(child.putanja, self.central_widget)
            tab.pocetna_strana = self
            tab.read()
        tab.table.model().lista_prikaz = child.model.lista_prikaz
        
        tab.btn_down.clicked.connect(self.otvori_tabelu_dete)
        tab.btn_up.clicked.connect(self.otvori_tabelu_roditelj)

        self.central_widget.removeTab(self.central_widget.currentIndex())
        self.central_widget.addTab(tab, child.meta_podaci[0])

        meta = ""
        for s in range(len(child.meta_podaci[0])):
            if child.meta_podaci[0][s].isupper():
                meta += "_" + child.meta_podaci[0][s].lower()
            else:
                meta += child.meta_podaci[0][s]
                
        meta = meta[1:len(meta)]
        meta = child.meta_podaci[2] + "\\" + meta
        if child.meta_podaci[1] == "serijska":
            meta += "_ser."
            
        elif child.meta_podaci[1] == "sekvencijalna":
            meta += "_sek."
            
        elif child.meta_podaci[1] == "sql":
            meta += "_meta_podaci_sql."

        meta += child.meta_podaci[3]

        self.lista_putanja.append(meta)
        self.lista_putanja.remove(self.lista_putanja[self.central_widget.currentIndex()])

    def izmena_u_datoteci(self):
        if self.central_widget.currentWidget() == None:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nijedna datoteka nije otvorena!")
            poruka.exec_()
            return
        elif not hasattr(self.central_widget.currentWidget().table, "selected_elem"):
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nijedan element nije selektovan!")
            poruka.exec_()
            return
            
        model = self.central_widget.currentWidget().table.model()
        selektovani_element = model.get_element(self.central_widget.currentWidget().table.selected_elem)
        prikaz = PrikazElementa(self.central_widget.currentWidget(), False, selektovani_element)
    
        prikaz.exec_()
        self.central_widget.currentWidget().column_resize()

    def dodavanje_u_datoteku(self):
        if self.central_widget.currentWidget() == None:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nijedna datoteka nije otvorena!")
            poruka.exec_()
            return
            
        prikaz = PrikazElementa(self.central_widget.currentWidget())
    
        prikaz.exec_()
        self.central_widget.currentWidget().column_resize()

    def otvori_pretragu(self):
        if self.central_widget.currentWidget() == None:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Trenutno nijedna datoteka nije otvorena!")
            poruka.exec_()
            return

        prikaz = PrikazElementa(self.central_widget.currentWidget(),True)
        prikaz.exec_()
        if len(prikaz.lista_atr) != 0 and len(prikaz.lista_kriterijuma) != 0:
            model = self.central_widget.currentWidget().table.model()
            model = pretraga(
                prikaz.lista_atr, prikaz.lista_kriterijuma,
                prikaz.lista_vece_manje, 
                self.central_widget.currentWidget().meta_podaci,
                False,
                self.central_widget.currentWidget())

            if len(model.lista_prikaz) == 0:
                poruka = QMessageBox()
                icon = QtGui.QIcon("src/ikonice/logo.jpg")
                poruka.setWindowIcon(icon)
                poruka.setWindowTitle("Upozorenje!")
                poruka.setText("Zadata pretraga nije pronasla vrednosti koje odgovaraju zadatim kriterijumima.")
                poruka.exec_()
                return

            self.central_widget.currentWidget().table.setModel(model)
            self.central_widget.currentWidget().table.selectRow(0)
        else:
            poruka = QMessageBox()
            icon = QtGui.QIcon("src/ikonice/logo.jpg")
            poruka.setWindowIcon(icon)
            poruka.setWindowTitle("Upozorenje!")
            poruka.setText("Niste zadali ni jedan kriterijum za pretragu, pretraga je prekinuta.")
            poruka.exec_()
            return
            
        self.central_widget.currentWidget().column_resize()

    def delete_tab(self, index):
        self.central_widget.setCurrentIndex(index)
        tab = self.central_widget.currentWidget()
        if tab.is_baza:
            for i in range(len(self.lista_baza)):
                if self.lista_baza[i] == tab.indeks:
                    self.lista_baza.pop(i)
                    break
            self.central_widget.removeTab(index)
            return
        if hasattr(tab, "sortirano") and len(tab.table.model().lista_prikaz) > 1:
            if tab.meta_podaci[1] == "serijska": 
                while True:
                    izabrano = -1
                    list_tuple = ()
                    lista = ["Samo stare podatke", "Samo nove podatke", "Ili i stare i nove podatke"]
                    for i in range(len(lista)):
                        list_tuple = list_tuple + (lista[i],)

                    input = QtWidgets.QInputDialog.getItem(
                        tab,
                        "Izbor",
                        "Posto ste sortirali tabelu\nIzaberite da li zelite da sacuvate:",
                        list_tuple,
                        0,
                        editable=False)
                        
                    if input[1]:
                        for i in range(len(lista)):
                            if lista[i].find(input[0]) != -1:
                                izabrano = i
                                break
                        break

                putanja = tab.meta_podaci[4]

                if izabrano == 2:
                    i = 0
                    while True:
                        del1 = tab.meta_podaci[4].rfind(".")
                        deo = tab.meta_podaci[4][0:del1]
                        nastavak = tab.meta_podaci[4][del1:len(tab.meta_podaci[4])]
                        if not os.path.exists(deo + "_new"+str(i+1) + nastavak):
                            putanja = deo + "_new"+str(i+1) + nastavak
                            break
                        i += 1

                if izabrano != 0:
                    with open(putanja, 'w', newline='') as f:
                        writer = csv.writer(f, delimiter = ",")
                        writer.writerow([tab.putanja_meta])
                        for i in range(len(tab.table.model().lista_prikaz)):
                            tekst = ""
                            for j in range(len(tab.table.model().nazivi_atributa)):
                                tekst += str(tab.table.model().lista_prikaz[i].__getattribute__(tab.table.model().nazivi_atributa[j]))
                                if j < len(tab.table.model().nazivi_atributa)-1:
                                    tekst += ","
                                
                            novi_red = tekst.split(",")
                            writer.writerow(novi_red)

        self.central_widget.removeTab(index)
        self.lista_putanja.remove(self.lista_putanja[index])

    def read(self, index):
        putanja = self.dock.model.filePath(index)
        if os.path.isdir(putanja):
            return

        ista_putanja = False
        for i in range(len(self.lista_putanja)):
            if putanja == self.lista_putanja[i]:
                ista_putanja = True
                return
        if not ista_putanja:
            self.lista_putanja.append(putanja)
            if putanja.find(".sql") != -1:
                self.connection = mysql.connector.connect(user="******", password="******", host="127.0.0.1", database="projekat")
                self.csor = self.connection.cursor()
                putanja = "podaci\metaPodaci\projekat_meta_podaci.csv"
                meta_podaci = citanje_meta_podataka(putanja, True)
                self.imena_tabela = meta_podaci[4].split(",")

                self.treeView = QTreeView()
                self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)
                self.treeView.customContextMenuRequested.connect(self.dugme_baza)
                
                model = QStandardItemModel(parent=self.dock.tree.sub_layout)
                item = QStandardItem("zatvori")
                item.setEditable(False)
                model.appendRow(item)
                for text in self.imena_tabela:
                    item = QStandardItem(text)
                    item.setEditable(False)
                    model.appendRow(item)
                
                self.treeView.setModel(model)
                self.treeView.clicked.connect(self.dugme_baza)
                self.treeView.PositionAtBottom

                si = self.dock.tree.sub_layout.sizeHint()
                si.setWidth(self.dock.width())
                si.setHeight(200)
                self.treeView.setFixedSize(si.width(), si.height())

                self.treeView.setHeaderHidden(True)
                self.dock.tree.sub_layout.addWidget(self.treeView)
                self.dock.tree.sub_layout.setAlignment(self.treeView, QtCore.Qt.AlignBottom)
                return

            tab = Tab(putanja, parent=self.central_widget)
            tab.pocetna_strana = self

            if putanja != "":
                tab.read()
            else:
                tab.read(putanja)
            tab.btn_down.clicked.connect(self.otvori_tabelu_dete)
            tab.btn_up.clicked.connect(self.otvori_tabelu_roditelj)
            tab.naziv = tab.meta_podaci[0]
            self.central_widget.addTab(tab, tab.naziv)
            self.central_widget.setCurrentIndex(self.central_widget.currentIndex()+1)
    
    def dugme_baza(self, model_indeks):
        indeks = model_indeks.row()
        if indeks == 0:
            self.treeView.close()
            for i in range(len(self.lista_putanja)):
                if self.lista_putanja[i].find(".sql") != 1:
                    self.lista_putanja.pop(i)
                    break

            self.central_widget.setCurrentIndex(indeks)
            brojac = 0
            while len(self.lista_baza) != 0:
                tab = self.central_widget.currentWidget()
                if tab.is_baza:
                    for i in range(brojac, len(self.lista_baza)):
                        if self.lista_baza[brojac] == tab.indeks:
                            self.lista_baza.pop(brojac)
                            self.central_widget.removeTab(indeks)
                            break
                        else:
                            brojac += 1
                else:
                    indeks += 1
                    self.central_widget.setCurrentIndex(indeks)

            self.csor.close()
            self.connection.close()
            return
        else:
            indeks -= 1
            for i in range(len(self.lista_baza)):
                if self.lista_baza[i] == indeks:
                    return
            self.lista_baza.append(indeks)

        tab = Tab(parent=self.central_widget)
        tab.pocetna_strana = self
        tab.indeks = indeks
        tab.naziv = self.imena_tabela[indeks]
        
        nova_putanja = "podaci\\metaPodaci\\"
        nova_putanja += self.imena_tabela[indeks][0].lower()
        
        for s in range(1, len(self.imena_tabela[indeks])):
            if self.imena_tabela[indeks][s].isupper():
                nova_putanja += "_" + self.imena_tabela[indeks][s].lower()
            else:
                nova_putanja += self.imena_tabela[indeks][s]
        
        nova_putanja += "_meta_podaci_sql.csv"

        tab.read(nova_putanja)
        tab.btn_down.clicked.connect(self.otvori_tabelu_dete)
        tab.btn_up.clicked.connect(self.otvori_tabelu_roditelj)
        self.central_widget.addTab(tab, tab.naziv)
        self.central_widget.setCurrentIndex(self.central_widget.currentIndex()+1)
class ClassTreeWidget(QDialog):
    """
    TreeView widget to show pyleecan classes structured by their inheritance together
    with the selected class description.
    """
    def __init__(self, keys=None, expand=True):
        super(ClassTreeWidget, self).__init__()
        self.setupUi()
        self.expandAll = expand
        self.setMinimumHeight(600)

        # === default variables ===
        self.classDict = ClassInfo().get_dict()
        self.keys = keys or ClassInfo().get_base_classes()  # TODO all classes
        self.selectionModel = self.treeView.selectionModel()

        # === Signals ===
        self.selectionModel.selectionChanged.connect(self.onSelectionChanged)
        self.treeView.doubleClicked.connect(self.onClassSelected)
        self.buttons.accepted.connect(self.accept)
        self.buttons.rejected.connect(self.reject)

        # === Generate content ===
        self.generate()

    def onClassSelected(self, index):
        """Method to accept the selection if a class was double clicked."""
        if index.isValid():
            self.accept()

    def onSelectionChanged(self, itSelection):
        """ """
        index = itSelection.indexes()[0]
        desc = index.model().itemFromIndex(index).data()
        self.text.setText(desc)

    def getSelectedClass(self):
        """Get the currently selected class by its name."""
        index = self.selectionModel.selectedIndexes()[0]
        return index.model().itemFromIndex(index).text()

    def setupUi(self):
        """Init. the UI."""
        self.setWindowIcon(QIcon(ICON))
        # === Widgets ===
        # TreeView
        model = QtGui.QStandardItemModel(0, 1)
        model.setHorizontalHeaderLabels(["Class"])

        self.treeView = QTreeView()
        self.treeView.rootNode = model.invisibleRootItem()
        self.treeView.setModel(model)
        self.treeView.setAlternatingRowColors(False)

        # size options
        # setting min. width in self.generate to fit content

        self.treeView.setContextMenuPolicy(Qt.CustomContextMenu)

        # Hide Debug Columns
        # self.treeView.hideColumn(1)

        # text output
        self.text = QLabel()
        self.text.setAlignment(Qt.AlignTop)
        self.text.setWordWrap(True)
        self.text.setMinimumWidth(300)

        # Splitters
        self.splitter = QSplitter(self)
        self.splitter.setStretchFactor(0, 0)
        self.splitter.setStretchFactor(1, 1)
        self.splitter.addWidget(self.treeView)
        self.splitter.addWidget(self.text)

        # dialog buttons
        self.buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )  # window to create confirmation and cancellation buttons

        # === Layout ===
        # Horizontal Div.
        layout = QVBoxLayout()

        # layout.setContentsMargins(0, 0, 0, 0)
        # layout.setSpacing(0)

        layout.addWidget(self.splitter)
        layout.addWidget(self.buttons)

        self.setLayout(layout)

    def generate(self):
        """Method to (recursively) build the tree (view) of the data object."""
        self.treeDict = dict()
        for key in self.keys:
            self.genTreeDict(key, self.treeDict)

        self.genTreeView(self.treeDict)

        # set first row active & expand all branches
        index = self.treeView.model().index(0, 0)
        self.treeView.setCurrentIndex(index)
        self.treeView.expandAll()
        wHint = self.treeView.sizeHintForColumn(0)
        self.treeView.setMinimumWidth(wHint)
        self.treeView.setColumnWidth(0, wHint)
        if not self.expandAll:
            self.treeView.collapseAll()

    def genTreeDict(self, key, parent):
        """Generate a dict structure of the classes recursively on the parent dict."""
        parent[key] = dict()
        for typ in self.classDict[key]["daughters"]:
            if key == self.classDict[typ]["mother"]:
                self.genTreeDict(typ, parent[key])

    def genTreeView(self, tree, parent=None):
        """Generate the view item structure on the parent item."""
        # init if root
        if parent is None:
            parent = self.treeView.rootNode
            self.treeView.rootNode.removeRows(
                0, self.treeView.rootNode.rowCount())

        for key, item in tree.items():
            desc = (
                f"Class: {key} \nPackage: {self.classDict[key]['package']}" +
                f"\nDescription: {self.classDict[key]['desc']}")
            row = self.addRow(parent, key, desc)

            if item:
                self.genTreeView(item, parent=row)

    def addRow(self, parent, name="", desc=""):
        """Add a new row to the parent item."""
        item = QtGui.QStandardItem(name)
        item.setEditable(False)
        item.setData(desc)
        parent.appendRow([item])
        return item