def __init__(self, parent): super().__init__(parent) self.lineNumberArea = QLineNumberArea(self) self.blockCountChanged.connect(self.updateLineNumberAreaWidth) self.updateRequest.connect(self.updateLineNumberArea) self.cursorPositionChanged.connect(self.highlightCurrentLine) self.updateLineNumberAreaWidth(0) self.parent = parent self.font = QFont() self.size = 12 self.dialog = MessageBox(self) self.menu_font = QFont() self.menu_font.setFamily("Iosevka") self.menu_font.setPointSize(10) self.font.setFamily(editor["editorFont"]) self.font.setPointSize(editor["editorFontSize"]) self.focused = None self.text = None self.replace_tabs = 4 self.setWordWrapMode(4) self.setFont(self.font) self.l = 0 self.highlightingRules = [] self.indexes = None self.setTabStopWidth(editor["TabWidth"]) self.createStandardContextMenu() self.setWordWrapMode(QTextOption.NoWrap)
def __init__(self, callback, app=None, palette=None): super().__init__() directoryFont = QFont() self.app = app self.palette = palette directoryFont.setFamily(editor["directoryFont"]) directoryFont.setPointSize(editor["directoryFontSize"]) self.open_callback = callback self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.openMenu) self.setFont(directoryFont) self.layout = QHBoxLayout() self.model = QFileSystemModel() self.setModel(self.model) self.model.setRootPath(QDir.rootPath()) self.setMaximumWidth(300) self.setIndentation(10) self.setAnimated(True) self.newFile() self.setSortingEnabled(True) self.setWindowTitle("Dir View") self.hideColumn(1) self.resize(200, 600) self.hideColumn(2) self.confirmation = MessageBox() self.hideColumn(3) # self.layout.addWidget(self) self.doubleClicked.connect(self.openFile)
def __init__(self): super().__init__() self.editor = Editor(self) self.editor.setReadOnly(True) self.custom = Customize() self.font = QFont() self.numbers = NumberBar(self.editor, index=self.custom.index) self.dialog = MessageBox() # self.font.setFamily(editor["editorFont"]) self.font.setFamily("Iosevka") self.terminateButton = QPushButton() self.terminateButton.setIcon(QIcon("resources/square.png")) self.terminateButton.clicked.connect(self.terminate) self.font.setPointSize(12) self.layout = QHBoxLayout() self.layout.addWidget(self.numbers) self.layout.addWidget(self.editor, 1) self.layout.addWidget(self.terminateButton) self.setLayout(self.layout) self.output = None self.setFocusPolicy(Qt.StrongFocus) self.error = None self.finished = False self.editor.setFont(self.font) self.process = QProcess() self.state = None self.process.readyReadStandardError.connect( self.onReadyReadStandardError) self.process.readyReadStandardOutput.connect( self.onReadyReadStandardOutput)
def __init__(self, parent=None): super().__init__(parent) self.fileName = None self.parent = parent self.debugging = False self.line = None self.column = None self.wordlist = [] self.searchtext = None self.font = QFont() self.font.setFamily("Inconsolata") self.pointSize = 12 # TODO: Make this customizable self.tabWidth = 4 # TODO: Make this customizable self.font.setPointSize(self.pointSize) self.dialog = MessageBox(self) self.verticalScrollBar().setStyleSheet(""" background-color: transparent; """) self.horizontalScrollBar().setStyleSheet(""" background-color: transparent; """) self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCaretForegroundColor(QColor("#FFFFFF")) self.setEdgeColumn(121) self.setEdgeMode(1) self.setEdgeColor(QColor("#8c8c8c")) self.setFont(self.font) self.setMarginSensitivity(1, True) self.markerDefine(QsciScintilla.RightArrow, 8) self.setMarkerBackgroundColor(QColor('#FF0000'), 8) self.indicator_number = 0 self.indicator_value = 222 self.indicator_color = QColor("#FF0000") self.draw_under_text = True # Initializing some stuff self.set_brace_colors(QColor("#98b4f9"), QColor("#edf40e"), QColor("#98b4f9"), QColor("red")) self.cursorPositionChanged.connect(self.change_col) self.textChanged.connect(self.check_lines) self.set_linenumbers(QFontMetrics(self.font)) self.setFoldMarginColors(QColor("#212121"), QColor("#212121")) self.set_indentation_settings(self.tabWidth)
def __init__(self, parent=None): super().__init__(parent) self.onStart(choiceIndex) self.status = QStatusBar(self) self.custom = Customize() # Initializing the main widget where text is displayed self.tab = Tabs(self.openFile, app, palette) self.tabsOpen = [] self.pic_opened = False if file is not None: self.openFile(file) self.fileNameChange() self.dialog = MessageBox() self.pyConsoleOpened = None self.setWindowIcon(QIcon('resources/Python-logo-notext.svg_.png') ) # Setting the window icon self.setWindowTitle('PyPad') # Setting the window title self.tab.tabs.currentChanged.connect(self.fileNameChange) self.search = DocumentSearch() self.os = sys.platform self.openpy() self.openterm() self.openterminal() self.new() self.newProject() self.findDocument() self.openProjectF() self.open() self.save() self.saveAs() self.customize() self.exit() self.dir_opened = False self._dir = None # Without this, the whole layout is broken self.setCentralWidget(self.tab) self.newFileCount = 0 # Tracking how many new files are opened self.files = None # Tracking the current file that is open self.pyFileOpened = False # Tracking if python file is opened, this is useful to delete highlighting self.cFileOpened = False self.initUI() # Main UI
def __init__(self, app=None, palette=None): super().__init__() self.app = app self.palette = palette self.setFixedSize(800, 600) with open("default.json", "r") as selectedIndex: self.index = selectedIndex.read() if self.index == "": self.index = 0 selectedIndex.close() self.conf = MessageBox() self.opened = False self.vbox = QVBoxLayout(self) # Creating the layout self.setWindowIcon(QIcon('resources/Python-logo-notext.svg_.png')) self.initUI()
def __init__(self, parent=None): super().__init__() self.parent = parent self.pressed = False self.font = QFont() self.dialog = MessageBox(self) # self.font.setFamily(editor["editorFont"]) self.font.setFamily("Iosevka") self.font.setPointSize(12) self.layout = QVBoxLayout() self.setLayout(self.layout) self.output = None self.setFocusPolicy(Qt.StrongFocus) self.error = None self.finished = False self.clicked = False self.process = QProcess() self.state = None self.process.readyReadStandardError.connect(self.onReadyReadStandardError) self.process.readyReadStandardOutput.connect(self.onReadyReadStandardOutput) self.add() # Add items to the layout
def __init__(self, parent): super().__init__(parent) self.parent = parent self.font = QFont() self.size = 12 self.dialog = MessageBox() self.menu_font = QFont() self.menu_font.setFamily("Iosevka") self.menu_font.setPointSize(10) self.font.setFamily(editor["editorFont"]) self.font.setPointSize(editor["editorFontSize"]) self.focused = None self.replace_tabs = 4 self.setWordWrapMode(4) self.setFont(self.font) self.l = 0 self.highlightingRules = [] self.indexes = None self.setTabStopWidth(editor["TabWidth"]) self.createStandardContextMenu() self.setWordWrapMode(QTextOption.NoWrap)
class Customize(QWidget): def __init__(self, app=None, palette=None): super().__init__() self.app = app self.palette = palette self.setFixedSize(800, 600) with open("default.json", "r") as selectedIndex: self.index = selectedIndex.read() if self.index == "": self.index = 0 selectedIndex.close() self.conf = MessageBox() self.opened = False self.vbox = QVBoxLayout(self) # Creating the layout self.setWindowIcon(QIcon('resources/Python-logo-notext.svg_.png')) self.initUI() def initUI(self): self.LayoutImage = QLabel(self) self.LayoutText = QLabel(self) self.hbox = QHBoxLayout() editor = config0['editor'] self.font = QFont() self.font.setFamily(editor["editorFont"]) self.font.setPointSize(editor["editorFontSize"]) self.combo = QComboBox(self) self.combo.addItem("Theme 1") self.combo.addItem("Theme 2") self.combo.addItem("Theme 3") self.combo.currentIndexChanged.connect(self.themes) self.combo.setFont(self.font) self.theme1 = QPixmap( 'resources/layout1.png') # These are the pictures of themes self.theme2 = QPixmap('resources/layout1.png') self.theme3 = QPixmap('resources/layout1.png') self.vbox.addWidget( self.combo ) # Adding Combobox to vertical boxlayout so it would look better self.LayoutText.setFont(self.font) self.hbox.addWidget(self.LayoutText) self.hbox.addWidget(self.LayoutImage) self.LayoutImage.setPixmap(self.theme1) # This is the "main" theme self.LayoutImage.resize(415, 287) self.LayoutText.setText("Dark theme") self.vbox.addLayout(self.hbox) self.selector = QPushButton(self) self.selector.setFixedSize(100, 30) self.selector.setLayoutDirection(Qt.RightToLeft) self.selector.setText("Select") self.selector.setFont(self.font) self.vbox.addWidget(self.selector) self.setLayout(self.vbox) def run(self): self.show() def themes(self, index): if index == 0: self.LayoutImage.setPixmap(self.theme1) self.LayoutText.setText("Dark theme") elif index == 1: self.LayoutImage.setPixmap(self.theme3) self.LayoutText.setText("Fancy theme") elif index == 2: self.LayoutImage.setPixmap(self.theme2) self.LayoutText.setText("Light theme") else: pass def test(self): index = self.combo.currentIndex() self.index = str(index) self.conf.confirmation(index + 1) with open("default.json", "w+") as write: write.write(str(self.index)) write.close() if index == 0 and self.app is not None and self.palette is not None: editor = config0['editor'] self.palette.setColor(QPalette.Window, QColor(editor["windowColor"])) self.palette.setColor(QPalette.WindowText, QColor(editor["windowText"])) self.palette.setColor(QPalette.Base, QColor(editor["editorColor"])) self.palette.setColor(QPalette.AlternateBase, QColor(editor["alternateBase"])) self.palette.setColor(QPalette.ToolTipBase, QColor(editor["ToolTipBase"])) self.palette.setColor(QPalette.ToolTipText, QColor(editor["ToolTipText"])) self.palette.setColor(QPalette.Text, QColor(editor["editorText"])) self.palette.setColor(QPalette.Button, QColor(editor["buttonColor"])) self.palette.setColor(QPalette.ButtonText, QColor(editor["buttonTextColor"])) self.palette.setColor(QPalette.Highlight, QColor(editor["HighlightColor"]).lighter()) self.palette.setColor(QPalette.HighlightedText, QColor(editor["HighlightedTextColor"])) elif index == 1 and self.app is not None and self.palette is not None: editor = config1['editor'] self.palette.setColor(QPalette.Window, QColor(editor["windowColor"])) self.palette.setColor(QPalette.WindowText, QColor(editor["windowText"])) self.palette.setColor(QPalette.Base, QColor(editor["editorColor"])) self.palette.setColor(QPalette.AlternateBase, QColor(editor["alternateBase"])) self.palette.setColor(QPalette.ToolTipBase, QColor(editor["ToolTipBase"])) self.palette.setColor(QPalette.ToolTipText, QColor(editor["ToolTipText"])) self.palette.setColor(QPalette.Text, QColor(editor["editorText"])) self.palette.setColor(QPalette.Button, QColor(editor["buttonColor"])) self.palette.setColor(QPalette.ButtonText, QColor(editor["buttonTextColor"])) self.palette.setColor(QPalette.Highlight, QColor(editor["HighlightColor"]).lighter()) self.palette.setColor(QPalette.HighlightedText, QColor(editor["HighlightedTextColor"])) elif index == 2 and self.app is not None or self.palette is not None: editor = config2['editor'] self.palette.setColor(QPalette.Window, QColor(editor["windowColor"])) self.palette.setColor(QPalette.WindowText, QColor(editor["windowText"])) self.palette.setColor(QPalette.Base, QColor(editor["editorColor"])) self.palette.setColor(QPalette.AlternateBase, QColor(editor["alternateBase"])) self.palette.setColor(QPalette.ToolTipBase, QColor(editor["ToolTipBase"])) self.palette.setColor(QPalette.ToolTipText, QColor(editor["ToolTipText"])) self.palette.setColor(QPalette.Text, QColor(editor["editorText"])) self.palette.setColor(QPalette.Button, QColor(editor["buttonColor"])) self.palette.setColor(QPalette.ButtonText, QColor(editor["buttonTextColor"])) self.palette.setColor(QPalette.Highlight, QColor(editor["HighlightColor"]).lighter()) self.palette.setColor(QPalette.HighlightedText, QColor(editor["HighlightedTextColor"])) self.app.setPalette(self.palette)
class Main(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.onStart(choiceIndex) self.status = QStatusBar(self) self.custom = Customize() # Initializing the main widget where text is displayed self.tab = Tabs(self.openFile, app, palette) self.tabsOpen = [] self.pic_opened = False if file is not None: self.openFile(file) self.fileNameChange() self.dialog = MessageBox() self.pyConsoleOpened = None self.setWindowIcon(QIcon('resources/Python-logo-notext.svg_.png') ) # Setting the window icon self.setWindowTitle('PyPad') # Setting the window title self.tab.tabs.currentChanged.connect(self.fileNameChange) self.search = DocumentSearch() self.os = sys.platform self.openPy() self.openTerm() self.new() self.newProject() self.findDocument() self.openProjectF() self.open() self.save() self.saveAs() self.customize() self.exit() self.dir_opened = False self._dir = None # Without this, the whole layout is broken self.setCentralWidget(self.tab) self.newFileCount = 0 # Tracking how many new files are opened self.files = None # Tracking the current file that is open self.pyFileOpened = False # Tracking if python file is opened, this is useful to delete highlighting self.cFileOpened = False self.initUI() # Main UI def fileNameChange(self): try: currentFileName = self.tab.tabs.currentWidget().baseName self.setWindowTitle("PyPad ~ " + str(currentFileName)) except AttributeError: self.setWindowTitle("PyPad ~ ") def onStart(self, index): if index == 0: editor = config0['editor'] elif index == 1: editor = config1['editor'] elif index == 2: editor = config2['editor'] else: editor = config0['editor'] if editor["windowStaysOnTop"] is True: self.setWindowFlags(Qt.WindowStaysOnTopHint) else: pass self.font = QFont() self.font.setFamily(editor["editorFont"]) self.font.setPointSize(editor["editorFontSize"]) self.tabSize = editor["TabWidth"] def initUI(self): self.setStatusBar(self.status) # Initializing the status bar self.font.setFixedPitch(True) menuFont = QFont() menuFont.setFamily(editor["menuFont"]) menuFont.setPointSize(editor['menuFontSize']) menu = self.menuBar() menu.setFont(menuFont) # Creating the file menu fileMenu = menu.addMenu('File') # Adding options to the file menu # self.setStatusBar(self.status) fileMenu.addAction(self.newAct) fileMenu.addAction(self.newProjectAct) fileMenu.addAction(self.openAct) fileMenu.addAction(self.openProjectAct) fileMenu.addAction(self.saveAct) fileMenu.addAction(self.saveAsAct) fileMenu.addSeparator() fileMenu.addAction(self.exitAct) toolMenu = menu.addMenu('Tools') toolMenu.addAction(self.openPyAct) toolMenu.addAction(self.openTermAct) appearance = menu.addMenu('Appearance') appearance.addAction(self.colorSchemeAct) searchDoc = menu.addMenu('Find document') searchDoc.addAction(self.findDocumentAct) self.showMaximized() def open(self): self.openAct = QAction('Open...', self) self.openAct.setShortcut('Ctrl+O') self.openAct.setStatusTip('Open a file') self.openAct.triggered.connect(self.openFileFromMenu) def new(self): self.newAct = QAction('New') self.newAct.setShortcut('Ctrl+N') self.newAct.setStatusTip('Create a new file') self.newAct.triggered.connect(self.newFile) def newProject(self): self.newProjectAct = QAction('New project') self.newProjectAct.setShortcut('Ctrl+Shift+N') self.newProjectAct.setStatusTip('Create a new project') self.newProjectAct.triggered.connect(self.newProjectFolder) def openProjectF(self): self.openProjectAct = QAction('Open project') self.openProjectAct.setShortcut('Ctrl+Shift+O') self.openProjectAct.setStatusTip('Open a project') self.openProjectAct.triggered.connect(self.openProject) def customize(self): self.colorSchemeAct = QAction('Customize', self) self.colorSchemeAct.setShortcut('Alt+C') self.colorSchemeAct.setStatusTip('Select a color scheme') self.colorSchemeAct.triggered.connect(self.theme) def theme(self): self.custom.run() self.custom.selector.clicked.connect(self.custom.test) def save(self): self.saveAct = QAction('Save') self.saveAct.setShortcut('Ctrl+S') self.saveAct.setStatusTip('Save a file') self.saveAct.triggered.connect(self.saveFile) def openPy(self): self.openPyAct = QAction('IPython console', self) self.openPyAct.setShortcut('Ctrl+Y') self.openPyAct.setStatusTip('Open IPython console') self.openPyAct.triggered.connect(self.Console) def openTerm(self): self.openTermAct = QAction('Run', self) self.openTermAct.setShortcut('Shift+F10') self.openTermAct.setStatusTip('Run your code') self.openTermAct.triggered.connect(self.Terminal) def saveAs(self): self.saveAsAct = QAction('Save As...') self.saveAsAct.setShortcut('Ctrl+Shift+S') self.saveAsAct.setStatusTip('Save a file as') self.saveAsAct.triggered.connect(self.saveFileAs) def findDocument(self): self.findDocumentAct = QAction('Find document') self.findDocumentAct.setShortcut('Ctrl+Shift+F') self.findDocumentAct.setStatusTip('Find a document') self.findDocumentAct.triggered.connect(self.findDocumentFunc) def findDocumentFunc(self): self.search.run() def exit(self): self.exitAct = QAction('Quit', self) self.exitAct.setShortcut('Ctrl+Q') self.exitAct.setStatusTip('Exit application') self.exitAct.triggered.connect(qApp.quit) def openFileFromMenu(self): options = QFileDialog.Options() filenames, _ = QFileDialog.getOpenFileNames( self, 'Open a file', '', 'All Files (*);;Python Files (*.py);;Text Files (*.txt)', options=options) if filenames: # If file is selected, we can open it filename = filenames[0] if filename[-3:] in ['gif', 'png', 'jpg', 'bmp' ] or filename[-4:] in ['jpeg']: self.pic_opened = True self.openFile(filename) def openFile(self, filename): try: for index, tabName in enumerate(self.tab.tabCounter): with open(filename, 'r+') as file_o: if filename[-3:] in ['gif', 'png', 'jpg', 'bmp' ] or filename[-4:] in ['jpeg']: self.pic_opened = True else: self.pic_opened = False try: text = file_o.read() except UnicodeDecodeError as E: text = str(E) basename = os.path.basename(filename) if not self.pic_opened: tab = Content(text, filename, basename, self.custom.index, self, ex) tab.saved = True tab.modified = False else: tab = Image(filename, basename) if tabName == tab.baseName: self.tab.tabs.removeTab(index) self.tab.tabCounter.remove(tab.baseName) try: with open(filename, 'r+') as file_o: try: if self.pic_opened is not True: text = file_o.read() else: text = None except (FileNotFoundError, UnicodeDecodeError, AttributeError) as E: text = str(E) except FileNotFoundError: with open(filename, 'w+') as newFileCreated: text = newFileCreated.read() basename = os.path.basename(filename) if self.pic_opened is True: tab = Image(filename, basename) else: tab = Content(text, filename, basename, self.custom.index, self, ex) # Creating a tab object *IMPORTANT* tab.saved = True tab.modified = False self.tab.tabCounter.append(tab.baseName) dirPath = os.path.dirname(filename) self.files = filename self.tabsOpen.append(self.files) index = self.tab.tabs.addTab( tab, tab.fileName ) # This is the index which we will use to set the current if not self.dir_opened: # If a project isnt opened then we open a directory everytime we open a file self.tab.directory.openDirectory(dirPath) self.tab.showDirectory() else: pass self.tab.setLayout(self.tab.layout) # Finally we set the layout self.tab.tabs.setCurrentIndex( index) # Setting the index so we could find the current widget self.currentTab = self.tab.tabs.currentWidget() if self.pic_opened is not True: self.currentTab.editor.setFont(self.font) # Setting the font self.currentTab.editor.setTabStopWidth( self.tabSize) # Setting tab size self.currentTab.editor.setFocus( ) # Setting focus to the tab after we open it self.pic_opened = False except (IsADirectoryError, AttributeError, UnboundLocalError, PermissionError) as E: print(E) def newFile(self): text = "" if self._dir: fileName = str(self._dir) + "/" + "Untitled_file_" + str( random.randint(1, 100)) + ".py" else: current = os.getcwd() fileName = current + "/" + "Untitled_file_" + str( random.randint(1, 100)) + ".py" self.pyFileOpened = True # Creates a new blank file file = Content(text, fileName, fileName, self.custom.index, self, ex) self.tab.splitterH.addWidget( self.tab.tabs ) # Adding tabs, now the directory tree will be on the left self.tab.tabCounter.append(file.fileName) self.tab.setLayout(self.tab.layout) # Finally we set the layout index = self.tab.tabs.addTab( file, file.fileName ) # addTab method returns an index for the tab that was added self.tab.tabs.setCurrentIndex( index) # Setting focus to the new tab that we created widget = self.tab.tabs.currentWidget() widget.editor.setFocus() widget.editor.setFont(self.font) widget.editor.setTabStopWidth(self.tabSize) def newProjectFolder(self): self.dialog = MessageBox() self.dialog.newProject() def openProject(self): self._dir = QFileDialog.getExistingDirectory(None, 'Select a folder:', '', QFileDialog.ShowDirsOnly) self.tab.directory.openDirectory(self._dir) self.dir_opened = True self.tab.showDirectory() def saveFile(self): try: active_tab = self.tab.tabs.currentWidget() if self.tab.tabs.count(): # If a file is already opened with open(active_tab.fileName, 'w+') as saveFile: saveFile.write(active_tab.editor.toPlainText()) active_tab.saved = True active_tab.modified = False saveFile.close() active_tab.tokenize_file() else: options = QFileDialog.Options() name = QFileDialog.getSaveFileName( self, 'Save File', '', 'All Files (*);;Python Files (*.py);;Text Files (*.txt)', options=options) fileName = name[0] with open(fileName, "w+") as saveFile: active_tab.saved = True active_tab.modified = False self.tabsOpen.append(fileName) saveFile.write(active_tab.editor.toPlainText()) saveFile.close() ex.setWindowTitle("PyPad ~ " + str(active_tab.baseName) + " [SAVED]") active_tab.tokenize_file() except Exception as E: print(E) def saveFileAs(self): try: active_tab = self.tab.tabs.currentWidget() if active_tab is not None: active_index = self.tab.tabs.currentIndex() options = QFileDialog.Options() name = QFileDialog.getSaveFileName( self, 'Save File', '', 'All Files (*);;Python Files (*.py);;Text Files (*.txt)', options=options) fileName = name[0] with open(fileName, "w+") as saveFile: active_tab.saved = True active_tab.modified = False self.tabsOpen.append(fileName) try: baseName = os.path.basename(fileName) except AttributeError: print("All tabs closed") saveFile.write(active_tab.editor.toPlainText()) text = active_tab.editor.toPlainText() newTab = Content(str(text), fileName, baseName, self.custom.index, self, ex) self.tab.tabs.removeTab( active_index ) # When user changes the tab name we make sure we delete the old one index = self.tab.tabs.addTab( newTab, newTab.fileName) # And add the new one! self.tab.tabs.setCurrentIndex(index) newActiveTab = self.tab.tabs.currentWidget() newActiveTab.editor.setFont(self.font) newActiveTab.editor.setFocus() if fileName.endswith( ".py" ): # If we are dealing with a python file we use highlighting on it self.pyhighlighter = PyHighlighter( newActiveTab.editor.document(), index=self.custom.index) newActiveTab.editor.setTabStopWidth(self.tabSize) saveFile.close() ex.setWindowTitle("PyPad ~ " + str(active_tab.baseName) + " [SAVED]") else: print("No file opened") except FileNotFoundError: print("File dialog closed") def Console(self): self.pyConsoleOpened = True self.ind = self.tab.splitterV.indexOf(self.tab.term) self.o = self.tab.splitterV.indexOf(self.tab.Console) if self.tab.splitterV.indexOf( self.tab.Console ) == -1: # If the Console widget DOESNT EXIST YET! self.tab.splitterV.addWidget(self.tab.term) self.ind = self.tab.splitterV.indexOf(self.tab.term) if self.tab.splitterV.indexOf( self.tab.term ) == -1: # If the terminal widget doesnt exist yet self.tab.splitterV.replaceWidget(self.o, self.tab.term) self.o = self.tab.splitterV.indexOf(self.tab.Console) self.ind = self.tab.splitterV.indexOf(self.tab.term) def Terminal(self): active_tab = self.tab.tabs.currentWidget() if self.pyConsoleOpened: self.o = self.tab.splitterV.indexOf(self.tab.Console) self.ind = self.tab.splitterV.indexOf(self.tab.term) if self.ind == -1: if platform.system() == "Linux": self.tab.Console.run("python3 " + active_tab.fileName) elif platform.system() == "Windows": self.tab.Console.run("python " + active_tab.fileName) else: self.tab.Console.run("python3 " + active_tab.fileName) else: self.tab.splitterV.replaceWidget(self.ind, self.tab.Console) try: if platform.system() == "Linux": self.tab.Console.run("python3 " + active_tab.fileName) elif platform.system() == "Windows": self.tab.Console.run("python " + active_tab.fileName) else: self.tab.Console.run("python3 " + active_tab.fileName) except AttributeError as E: print(E) else: self.tab.splitterV.addWidget(self.tab.Console) try: active_tab = self.tab.tabs.currentWidget() if platform.system() == "Linux": self.tab.Console.run("python3 " + active_tab.fileName) elif platform.system() == "Windows": self.tab.Console.run("python " + active_tab.fileName) else: self.tab.Console.run("python3 " + active_tab.fileName) except AttributeError as E: print(E)
class Tabs(QWidget): def __init__(self, callback, app, palette): super().__init__() self.app = app self.palette = palette self.layout = QHBoxLayout(self) # Initialize tab screen self.tabs = QTabWidget() font = QFont(editor['tabFont']) font.setPointSize( editor["tabFontSize"]) # This is the tab font and font size self.tabs.setFont(font) self.dialog = MessageBox() self.tabs.usesScrollButtons() self.filelist = [] self.tabSaved = False self.Console = Terminal( ) # This is the terminal widget and the SECOND thread self.term = IPythonWidget() self.directory = Directory(callback, self.app, self.palette) # TODO: This is top left self.directory.clearSelection() self.tabCounter = [] # Add tabs self.tab_layout = QHBoxLayout( ) # Create new layout for original tab layout self.tab_layout.addWidget(self.tabs) # Add tab widget to tab layout self.search_layout = QHBoxLayout() self.tabs.setTabsClosable(True) self.tabs.setMovable( editor['tabMovable']) # Let's you make the tabs movable if editor[ 'tabShape'] is True: # If tab shape is true then they have this rounded look self.tabs.setTabShape(1) else: self.tabs.setTabShape(0) # If false, it has this boxy look self.tabs.tabCloseRequested.connect(self.closeTab) # Add Console self.console_layout = QHBoxLayout() # Create console layout self.console_layout.addWidget( self.term) # Add console to console layout # Build Layout self.layout.addLayout( self.tab_layout) # Adds 'TOP' layout : tab + directory self.layout.addLayout(self.search_layout) # Creating horizontal splitter self.splitterH = QSplitter(Qt.Horizontal) # Creating vertical splitter self.splitterV = QSplitter(Qt.Vertical) self.splitterV.addWidget(self.splitterH) self.layout.addWidget(self.splitterV) self.splitterV.setSizes([300, 10]) self.setLayout(self.layout) # Sets layout of QWidget self.closeShortcut = QShortcut( QKeySequence(editor["closeTabShortcut"]), self) self.closeShortcut.activated.connect(self.closeTabShortcut) self.getAllOpenTabs = QShortcut(QKeySequence("Ctrl+Shift+W"), self) self.getAllOpenTabs.activated.connect(self.getAllOpenTabsFunc) currentTab = self.tabs.currentWidget() self.hideDirectory() """ def hideDirectory(self): self.tab_layout.removeWidget(self.directory) self.directory.setVisible(False)""" @pyqtSlot() def closeTabShortcut(self): self.index = self.tabs.currentIndex() self.closeTab(self.index) def getAllOpenTabsFunc(self): word = 'import' for tab in range(self.tabs.count()): file = self.tabs.widget(tab).fileName if file not in self.filelist: self.filelist.append(file) for file in self.filelist: openedFileContents = open(file, 'r').read() def closeTab(self, index): try: tab = self.tabs.widget(index) if tab.saved is True and tab.modified is False: tab.deleteLater() self.tabCounter.pop(index) self.filelist.pop(index) self.tabs.removeTab(index) elif tab.modified is True: self.dialog.saveMaybe(tab, self.tabCounter, self.tabs, index) except (AttributeError, IndexError) as E: try: tab.deleteLater() self.tabCounter.pop(index) self.filelist.pop(index) self.tabs.removeTab(index) except (AttributeError, IndexError) as E: print(E) def showDirectory(self): self.directory.setVisible(True) self.tab_layout.removeWidget(self.tabs) self.splitterH.addWidget( self.directory ) # Adding that directory widget in the Tab class BEFORE the tabs self.splitterH.addWidget( self.tabs ) # Adding tabs, now the directory tree will be on the left def hideDirectory(self): self.tab_layout.removeWidget(self.directory) self.directory.setVisible(False) """ Because the root layouts are set all you have to do now is just add/remove widgets from the parent layout associated. This keeps the UI order set as intended as built above when initialized. """ def showConsole(self): pass def currentTab(self): return self.tabs.currentWidget()
class Console(QWidget): errorSignal = pyqtSignal(str) outputSignal = pyqtSignal(str) def __init__(self): super().__init__() self.editor = Editor(self) self.editor.setReadOnly(True) self.custom = Customize() self.font = QFont() self.numbers = NumberBar(self.editor, index=self.custom.index) self.dialog = MessageBox() # self.font.setFamily(editor["editorFont"]) self.font.setFamily("Iosevka") self.terminateButton = QPushButton() self.terminateButton.setIcon(QIcon("resources/square.png")) self.terminateButton.clicked.connect(self.terminate) self.font.setPointSize(12) self.layout = QHBoxLayout() self.layout.addWidget(self.numbers) self.layout.addWidget(self.editor, 1) self.layout.addWidget(self.terminateButton) self.setLayout(self.layout) self.output = None self.setFocusPolicy(Qt.StrongFocus) self.error = None self.finished = False self.editor.setFont(self.font) self.process = QProcess() self.state = None self.process.readyReadStandardError.connect( self.onReadyReadStandardError) self.process.readyReadStandardOutput.connect( self.onReadyReadStandardOutput) def onReadyReadStandardError(self): try: self.error = self.process.readAllStandardError().data().decode() self.editor.appendPlainText(self.error) self.errorSignal.emit(self.error) if self.error == "": pass else: self.error = self.error.split(os.linesep)[-2] self.dialog.helpword = str(self.error) self.dialog.getHelp() except IndexError as E: print(E) def onReadyReadStandardOutput(self): try: self.result = self.process.readAllStandardOutput().data().decode() except UnicodeDecodeError as E: print(E) self.editor.appendPlainText(self.result.strip("\n")) self.state = self.process.state() self.outputSignal.emit(self.result) def ifFinished(self, exitCode, exitStatus): self.finished = True def run(self, command): """Executes a system command.""" # clear previous text self.editor.clear() # self.editor.setPlainText("[" + str(getpass.getuser()) + "@" + str( socket.gethostname()) + "]" + #" ~/" + str(os.path.basename(os.getcwd())) + " >$") if self.process.state() == 1 or self.process.state() == 2: self.process.kill() self.editor.setPlainText("Process already started, terminating") else: self.process.start(command) def terminate(self): if self.process.state() == 2: self.process.kill()
def __init__(self, callback, app, palette): super().__init__() self.app = app self.palette = palette self.layout = QHBoxLayout(self) # Initialize tab screen self.tabs = QTabWidget() font = QFont(editor['tabFont']) font.setPointSize( editor["tabFontSize"]) # This is the tab font and font size self.tabs.setFont(font) self.dialog = MessageBox() self.tabs.usesScrollButtons() self.filelist = [] self.tabSaved = False self.Console = Terminal( ) # This is the terminal widget and the SECOND thread self.term = IPythonWidget() self.directory = Directory(callback, self.app, self.palette) # TODO: This is top left self.directory.clearSelection() self.tabCounter = [] # Add tabs self.tab_layout = QHBoxLayout( ) # Create new layout for original tab layout self.tab_layout.addWidget(self.tabs) # Add tab widget to tab layout self.search_layout = QHBoxLayout() self.tabs.setTabsClosable(True) self.tabs.setMovable( editor['tabMovable']) # Let's you make the tabs movable if editor[ 'tabShape'] is True: # If tab shape is true then they have this rounded look self.tabs.setTabShape(1) else: self.tabs.setTabShape(0) # If false, it has this boxy look self.tabs.tabCloseRequested.connect(self.closeTab) # Add Console self.console_layout = QHBoxLayout() # Create console layout self.console_layout.addWidget( self.term) # Add console to console layout # Build Layout self.layout.addLayout( self.tab_layout) # Adds 'TOP' layout : tab + directory self.layout.addLayout(self.search_layout) # Creating horizontal splitter self.splitterH = QSplitter(Qt.Horizontal) # Creating vertical splitter self.splitterV = QSplitter(Qt.Vertical) self.splitterV.addWidget(self.splitterH) self.layout.addWidget(self.splitterV) self.splitterV.setSizes([300, 10]) self.setLayout(self.layout) # Sets layout of QWidget self.closeShortcut = QShortcut( QKeySequence(editor["closeTabShortcut"]), self) self.closeShortcut.activated.connect(self.closeTabShortcut) self.getAllOpenTabs = QShortcut(QKeySequence("Ctrl+Shift+W"), self) self.getAllOpenTabs.activated.connect(self.getAllOpenTabsFunc) currentTab = self.tabs.currentWidget() self.hideDirectory() """
class Directory(QTreeView): def __init__(self, callback, app=None, palette=None): super().__init__() directoryFont = QFont() self.app = app self.palette = palette directoryFont.setFamily(editor["directoryFont"]) directoryFont.setPointSize(editor["directoryFontSize"]) self.open_callback = callback self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.openMenu) self.setFont(directoryFont) self.layout = QHBoxLayout() self.model = QFileSystemModel() self.setModel(self.model) self.model.setRootPath(QDir.rootPath()) self.setMaximumWidth(300) self.setIndentation(10) self.setAnimated(True) self.newFile() self.setSortingEnabled(True) self.setWindowTitle("Dir View") self.hideColumn(1) self.resize(200, 600) self.hideColumn(2) self.confirmation = MessageBox() self.hideColumn(3) # self.layout.addWidget(self) self.doubleClicked.connect(self.openFile) def newFile(self): self.newAct = QAction('New') self.newAct.setStatusTip('Create a new file') self.newAct.triggered.connect(lambda: print("new file created lol")) def openMenu(self, position): indexes = self.selectedIndexes() if len(indexes) > 0: level = 0 index = indexes[0] while index.parent().isValid(): index = index.parent() level += 1 menu = QMenu() menu.addAction( self.newAct ) # TODO: Add more context menu stuff and make them functional menu.exec_(self.viewport().mapToGlobal(position)) def focusInEvent(self, event): # If we are focused then we change the selected item highlighting color self.focused = True self.palette.setColor(QPalette.Highlight, QColor(editor["HighlightColor"]).lighter()) self.app.setPalette(self.palette) def focusOutEvent(self, event): # If we un focus from the QTreeView then we make the highlighted item color white self.palette.setColor( QPalette.Highlight, QColor(editor["UnfocusedHighlightColor"]).lighter()) # self.clearSelection() Uncomment this if you want to remove all highlighting when unfocused self.app.setPalette(self.palette) def openDirectory(self, path): self.setRootIndex(self.model.index(path)) def openFile(self, signal): file_path = self.model.filePath(signal) self.open_callback(file_path) return file_path def keyPressEvent(self, event): key = event.key() if key == Qt.Key_Delete: try: self.fileObject = self.selectedIndexes()[0] fileName = self.model.filePath(self.fileObject) self.confirmation.run("Are you sure you want to delete ", str(fileName)) except IndexError: print("No file selected")
class Editor(QPlainTextEdit): def __init__(self, parent): super().__init__(parent) self.lineNumberArea = QLineNumberArea(self) self.blockCountChanged.connect(self.updateLineNumberAreaWidth) self.updateRequest.connect(self.updateLineNumberArea) self.cursorPositionChanged.connect(self.highlightCurrentLine) self.updateLineNumberAreaWidth(0) self.parent = parent self.font = QFont() self.size = 12 self.dialog = MessageBox(self) self.menu_font = QFont() self.menu_font.setFamily("Iosevka") self.menu_font.setPointSize(10) self.font.setFamily(editor["editorFont"]) self.font.setPointSize(editor["editorFontSize"]) self.focused = None self.text = None self.replace_tabs = 4 self.setWordWrapMode(4) self.setFont(self.font) self.l = 0 self.highlightingRules = [] self.indexes = None self.setTabStopWidth(editor["TabWidth"]) self.createStandardContextMenu() self.setWordWrapMode(QTextOption.NoWrap) def getTextCursor(self): textCursor = self.textCursor() textCursorPos = textCursor.position() return textCursor, textCursorPos def get_linenumbers(self): return self.blockCount() def check(self): cursor = self.textCursor() b = cursor.block() extraSelections = [] if len(b.text()) > 120: selection = QTextEdit.ExtraSelection() # TODO: implement something using flake8 selection.format.setFontUnderline(True) selection.format.setUnderlineColor(QColor("#FF0000")) selection.format.setUnderlineStyle( QTextCharFormat.SpellCheckUnderline) selection.format.setProperty(QTextFormat.FullWidthSelection, True) selection.cursor = self.textCursor() selection.cursor.clearSelection() extraSelections.append(selection) self.setExtraSelections(extraSelections) font = QFont() font.setFamily("Iosevka") font.setPointSize(10) QToolTip.setFont(font) cursor = self.textCursor() current_line = cursor.positionInBlock() QToolTip.showText( QCursor.pos(), "Line too long (" + str(current_line) + "> 120) | violation on line: " + str(b.blockNumber() + 1)) def newFile(self): """This is a wrapper for the function defined in Main""" self.new_action = QAction("New") self.new_action.triggered.connect(self.parent.parent.newFile) def lineNumberAreaWidth(self): digits = 1 max_value = max(1, self.blockCount()) while max_value >= 10: max_value /= 10 digits += 1 space = 10 + self.fontMetrics().width('9') * digits return space def updateLineNumberAreaWidth(self, _): self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0) def updateLineNumberArea(self, rect, dy): if dy: self.lineNumberArea.scroll(0, dy) else: self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height()) if rect.contains(self.viewport().rect()): self.updateLineNumberAreaWidth(0) def resizeEvent(self, event): super().resizeEvent(event) cr = self.contentsRect() self.lineNumberArea.setGeometry( QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height())) def highlightCurrentLine(self): extraSelections = [] if not self.isReadOnly(): selection = QTextEdit.ExtraSelection() lineColor = QColor("#434343") selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) selection.cursor = self.textCursor() selection.cursor.clearSelection() extraSelections.append(selection) self.setExtraSelections(extraSelections) self.check() def lineNumberAreaPaintEvent(self, event): painter = QPainter(self.lineNumberArea) painter.fillRect(event.rect(), QColor("#303030")) block = self.firstVisibleBlock() blockNumber = block.blockNumber() top = self.blockBoundingGeometry(block).translated( self.contentOffset()).top() bottom = top + self.blockBoundingRect(block).height() # Just to make sure I use the right font height = self.fontMetrics().height() while block.isValid() and (top <= event.rect().bottom()): if block.isVisible() and (bottom >= event.rect().top()): number = str(blockNumber + 1) painter.setPen(Qt.white) painter.drawText(0, top, self.lineNumberArea.width(), height, Qt.AlignCenter, number) block = block.next() top = bottom bottom = top + self.blockBoundingRect(block).height() blockNumber += 1 def openFile(self): self.open_action = QAction("Open") self.open_action.triggered.connect(self.parent.parent.openFileFromMenu) def runFile(self): self.run_action = QAction("Run") self.run_action.triggered.connect(self.parent.parent.terminal) def textUnderCursor(self): textCursor = self.textCursor() textCursor.select(QTextCursor.WordUnderCursor) return textCursor.selectedText() def contextMenuEvent(self, event): menu = QMenu() """Initializing actions""" self.newFile() self.openFile() self.runFile() menu.addAction(self.new_action) menu.addAction(self.open_action) menu.addAction(self.run_action) menu.setFont(self.menu_font) menu.exec(event.globalPos()) del menu def moveCursorPosBack(self): textCursor = self.textCursor() textCursorPos = textCursor.position() textCursor.setPosition(textCursorPos - 1) self.setTextCursor(textCursor) def mousePressEvent(self, e): self.check() if QApplication.queryKeyboardModifiers() == Qt.ControlModifier: if self.text is not None: self.check_func("lol") url = "https://docs.python.org/3/library/functions.html#" + self.text word = self.text self.parent.parent.showBrowser(url, word) QApplication.restoreOverrideCursor() super().mousePressEvent(e) def check_func(self, word): funcs = [ "abs", "all", "any", "ascii", "bin", "bool", "breakpoint", "bytearray", "bytes", "callable", "chr", "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "exec", "filter", "float", "format", "frozenset", "getattr", "globals", "hasattr", "hash", "help", "hex", "id", "input", "int", "isinstance", "issubclass", "iter", "len", "list", "locals", "map", "max", "memoryview", "min", "next", "object", "oct", "open", "ord", "pow", "print", "property", "range", "repr", "reversed", "round", "set", "setattr", "slice", "sorted", "staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip", "__import__" ] word_array = list(word) for wo in word_array: if wo in ["{", "}", "'", '"', "[", "]", "(", ")"]: word_array.remove(wo) for w in funcs: if w == "".join(word_array): print("".join(word_array)) return True def mouseReleaseEvent(self, e): self.check() super().mouseReleaseEvent(e) def keyPressEvent(self, e): textCursor = self.textCursor() key = e.key() if key == Qt.Key_H: # self.parent.completer.wordList # TODO: implement dynamic completion pass if e.modifiers( ) == Qt.ControlModifier and key == 16777217: # that key code stands for tab self.parent.parent.switchTabs() isSearch = (e.modifiers() == Qt.ControlModifier and e.key() == Qt.Key_F) if isSearch: try: currentWidget = self.parent currentFile = currentWidget.fileName currentEditor = currentWidget.editor textCursor = currentEditor.textCursor() textCursorPos = textCursor.position() except (AttributeError, UnboundLocalError) as E: print(E, " on line 228 in the file Editor.py") if currentWidget is not None: text, okPressed = QInputDialog.getText(self, 'Find', 'Find what: ') if okPressed: if text == "": text = " " self.dialog.noMatch(text) self.searchtext = text try: with open(currentFile, 'r') as file: contents = file.read() self.indexes = list(find_all(contents, text)) if len(self.indexes) == 0: self.dialog.noMatch(text) except FileNotFoundError as E: print(E, " on line 245 in the file Editor.py") if key == Qt.Key_QuoteDbl: self.insertPlainText('"') self.moveCursorPosBack() if key == 16777249: # This code stands for CTRL if self.check_func(self.textUnderCursor()): extraSelections = [] selection = QTextEdit.ExtraSelection() selection.format.setFontUnderline(True) selection.format.setUnderlineColor(QColor("#00d2ff")) selection.format.setBackground(QColor("#00d2ff")) selection.format.setProperty(QTextFormat.FullWidthSelection, True) selection.cursor = self.textCursor() selection.cursor.clearSelection() selection.cursor.select(QTextCursor.WordUnderCursor) extraSelections.append(selection) self.setExtraSelections(extraSelections) self.text = self.textUnderCursor() QApplication.setOverrideCursor(Qt.PointingHandCursor) else: self.text = None if e.modifiers() == Qt.ControlModifier and e.key( ) == 61: # Press Ctrl+Equal key to make font bigger self.font.setPointSize(self.size + 1) self.font.setFamily(editor["editorFont"]) self.setFont(self.font) self.size += 1 if e.modifiers() == Qt.ControlModifier and e.key() == 16777217: return if e.modifiers() == Qt.ControlModifier and e.key( ) == 45: # Press Ctrl+Minus key to make font smaller self.font.setPointSize(self.size - 1) self.font.setFamily(editor["editorFont"]) self.setFont(self.font) self.size -= 1 if key == Qt.Key_F3: try: index = self.indexes[0 + self.l] currentWidget = self.parent currentFile = currentWidget.fileName currentEditor = currentWidget.editor textCursor.setPosition(index) textCursor.movePosition(textCursor.Right, textCursor.KeepAnchor, len(self.searchtext)) currentEditor.setTextCursor(textCursor) self.l += 1 except IndexError: self.l = 0 if key == 39: self.insertPlainText("'") self.moveCursorPosBack() if key == Qt.Key_BraceLeft: self.insertPlainText("}") self.moveCursorPosBack() if key == Qt.Key_BracketLeft: self.insertPlainText("]") self.moveCursorPosBack() if key == Qt.Key_ParenLeft: self.insertPlainText(")") self.moveCursorPosBack() if key == Qt.Key_ParenRight: textCursor = self.textCursor() textCursor.select(QTextCursor.WordUnderCursor) if textCursor.selectedText( ) == "()" or "()" in textCursor.selectedText(): return if key == Qt.Key_BraceRight: textCursor = self.textCursor() textCursor.select(QTextCursor.WordUnderCursor) if textCursor.selectedText == "": return if key == 16777219: if self.textUnderCursor() in ['""', '()', '[]', "''", "{}"]: textCursor = self.textCursor() textCursorPos = textCursor.position() textCursor.setPosition(textCursorPos + 1) textCursor.deletePreviousChar() self.setTextCursor(textCursor) if key not in [16777217, 16777219, 16777220]: super().keyPressEvent(e) return e.accept() cursor = self.textCursor() if key == 16777217: # and self.replace_tabs: amount = 4 - self.textCursor().positionInBlock() % 4 self.insertPlainText(' ' * amount) elif key == 16777219 and cursor.selectionStart() == cursor.selectionEnd() and self.replace_tabs and \ cursor.positionInBlock(): position = cursor.positionInBlock() end = cursor.position() start = end - (position % 4) if start == end and position >= 4: start -= 4 string = self.toPlainText()[start:end] if not len(string.strip() ): # if length is 0 which is binary for false for i in range(end - start): cursor.deletePreviousChar() else: super().keyPressEvent(e) elif key == 16777220: end = cursor.position() start = end - cursor.positionInBlock() line = self.toPlainText()[start:end] indentation = len(line) - len(line.lstrip()) chars = '\t' if self.replace_tabs: chars = ' ' indentation /= self.replace_tabs if line.endswith(':'): if self.replace_tabs: indentation += 1 super().keyPressEvent(e) self.insertPlainText(chars * int(indentation)) else: super().keyPressEvent(e)
class Console(QWidget): errorSignal = pyqtSignal(str) outputSignal = pyqtSignal(str) def __init__(self, parent=None): super().__init__() self.parent = parent self.pressed = False self.font = QFont() self.dialog = MessageBox(self) # self.font.setFamily(editor["editorFont"]) self.font.setFamily("Iosevka") self.font.setPointSize(12) self.layout = QVBoxLayout() self.setLayout(self.layout) self.output = None self.setFocusPolicy(Qt.StrongFocus) self.error = None self.finished = False self.clicked = False self.process = QProcess() self.state = None self.process.readyReadStandardError.connect(self.onReadyReadStandardError) self.process.readyReadStandardOutput.connect(self.onReadyReadStandardOutput) self.add() # Add items to the layout def ispressed(self): return self.pressed def added(self): self.pressed = True def remove(self): self.parent.hideFileExecuter() self.clicked = True def hideTerminalClicked(self): return self.clicked def onReadyReadStandardError(self): try: self.error = self.process.readAllStandardError().data().decode() self.editor.appendPlainText(self.error) self.errorSignal.emit(self.error) if self.error == "": pass else: self.error = self.error.split(os.linesep)[-2] self.dialog.helpword = str(self.error) self.dialog.getHelp(self.parent.parent) except IndexError as E: print(E, " on line 70 in the file Console.py") def onReadyReadStandardOutput(self): try: self.result = self.process.readAllStandardOutput().data().decode() except UnicodeDecodeError as E: print(E, " on line 76 in the file Console.py") try: self.editor.appendPlainText(self.result.strip("\n")) self.state = self.process.state() except RuntimeError: pass self.outputSignal.emit(self.result) def ifFinished(self, exitCode, exitStatus): self.finished = True def add(self): """Executes a system command.""" # clear previous text self.added() self.button = QPushButton("Hide terminal") self.button.setFont(QFont("Iosevka", 11)) self.button.setStyleSheet(""" height: 20; background-color: #212121; """) self.terminateButton = QPushButton(" Stop") self.terminateButton.setIcon(QIcon("resources/square.png")) self.terminateButton.setFont(QFont("Iosevka", 11)) self.terminateButton.clicked.connect(self.terminate) self.button.setFixedWidth(120) self.h_layout = QHBoxLayout() self.editor = Editor(self) self.editor.setReadOnly(True) self.editor.setFont(self.font) self.layout.addWidget(self.button) self.layout.addWidget(self.editor) self.layout.addWidget(self.terminateButton) self.button.clicked.connect(self.remove) def run(self, command, path): # Takes in the command and the path of the file os.chdir(os.path.dirname(path)) # We need to change the path to the path where the file is being ran from self.editor.clear() if self.process.state() == 1 or self.process.state() == 2: self.process.kill() self.editor.setPlainText("Process already started, terminating") else: self.process.start(command) def terminate(self): if self.process.state() == 2: self.process.kill()
class Main(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.onStart(choiceIndex) self.status = QStatusBar(self) # Initializing the main widget where text is displayed self.tab = Tabs(self.openFile, app, palette, self) self.tabsOpen = [] self.pic_opened = False if file is not None: self.openFile(file) self.fileNameChange() self.dialog = MessageBox(self) self.setWindowIcon(QIcon('resources/Python-logo-notext.svg_.png') ) # Setting the window icon self.setWindowTitle('PyPad') # Setting the window title self.os = platform.system() self.tab.tabs.currentChanged.connect(self.fileNameChange) self.search = DocumentSearch() self.openterm() self.openterminal() self.new() self.newProject() self.findDocument() self.openProjectF() self.open() self.save() self.saveAs() self.exit() self.dir_opened = False self._dir = None # Without this, the whole layout is broken self.setCentralWidget(self.tab) self.files = None # Tracking the current file that is open # self.pyFileOpened = False # Tracking if python file is opened, this is useful to delete highlighting self.cFileOpened = False self.initUI() # Main UI def fileNameChange(self): try: currentFileName = self.tab.tabs.currentWidget().baseName self.setWindowTitle("PyPad ~ " + str(currentFileName)) except AttributeError: self.setWindowTitle("PyPad ~ ") def onStart(self, index): if index == 0: editor = config0['editor'] elif index == 1: editor = config1['editor'] elif index == 2: editor = config2['editor'] else: editor = config0['editor'] if editor["windowStaysOnTop"] is True: self.setWindowFlags(Qt.WindowStaysOnTopHint) else: pass self.font = QFont() self.font.setFamily(editor["editorFont"]) self.font.setPointSize(editor["editorFontSize"]) self.tabSize = editor["TabWidth"] def initUI(self): self.setStatusBar(self.status) # Initializing the status bar self.font.setFixedPitch(True) menuFont = QFont() menuFont.setFamily(editor["menuFont"]) menuFont.setPointSize(editor['menuFontSize']) menu = self.menuBar() menu.setFont(menuFont) # Creating the file menu fileMenu = menu.addMenu('File') # Adding options to the file menu # self.setStatusBar(self.status) fileMenu.addAction(self.newAct) fileMenu.addAction(self.newProjectAct) fileMenu.addAction(self.openAct) fileMenu.addAction(self.openProjectAct) fileMenu.addAction(self.saveAct) fileMenu.addAction(self.saveAsAct) fileMenu.addSeparator() fileMenu.addAction(self.exitAct) toolMenu = menu.addMenu('Tools') toolMenu.addAction(self.openTermAct) toolMenu.addAction(self.openTerminalAct) searchDoc = menu.addMenu('Find document') searchDoc.addAction(self.findDocumentAct) self.showMaximized() def open(self): self.openAct = QAction('Open...', self) self.openAct.setShortcut('Ctrl+O') self.openAct.setStatusTip('Open a file') self.openAct.triggered.connect(self.openFileFromMenu) def new(self): self.newAct = QAction('New') self.newAct.setShortcut('Ctrl+N') self.newAct.setStatusTip('Create a new file') self.newAct.triggered.connect(self.newFile) def newProject(self): self.newProjectAct = QAction('New project') self.newProjectAct.setShortcut('Ctrl+Shift+N') self.newProjectAct.setStatusTip('Create a new project') self.newProjectAct.triggered.connect(self.newProjectFolder) def openProjectF(self): self.openProjectAct = QAction('Open project') self.openProjectAct.setShortcut('Ctrl+Shift+O') self.openProjectAct.setStatusTip('Open a project') self.openProjectAct.triggered.connect(self.openProject) def switchTabs(self): if self.tab.tabs.count() - 1 == self.tab.tabs.currentIndex(): self.tab.tabs.setCurrentIndex(0) else: self.tab.tabs.setCurrentIndex(self.tab.tabs.currentIndex() + 1) def save(self): self.saveAct = QAction('Save') self.saveAct.setShortcut('Ctrl+S') self.saveAct.setStatusTip('Save a file') self.saveAct.triggered.connect(self.saveFile) def openterm(self): self.openTermAct = QAction('Run', self) self.openTermAct.setShortcut('Shift+F10') self.openTermAct.setStatusTip('Run your code') self.openTermAct.triggered.connect(self.execute_file) def openterminal(self): self.openTerminalAct = QAction("Terminal", self) self.openTerminalAct.setShortcut("Ctrl+T") self.openTerminalAct.setStatusTip("Open a terminal") self.openTerminalAct.triggered.connect(self.realterminal) def saveAs(self): self.saveAsAct = QAction('Save As...') self.saveAsAct.setShortcut('Ctrl+Shift+S') self.saveAsAct.setStatusTip('Save a file as') self.saveAsAct.triggered.connect(self.saveFileAs) def findDocument(self): self.findDocumentAct = QAction('Find document') self.findDocumentAct.setShortcut('Ctrl+Shift+F') self.findDocumentAct.setStatusTip('Find a document') self.findDocumentAct.triggered.connect(self.temp) def temp(self): pass def findDocumentFunc(self): self.search.run() def exit(self): self.exitAct = QAction('Quit', self) self.exitAct.setShortcut('Ctrl+Q') self.exitAct.setStatusTip('Exit application') self.exitAct.triggered.connect(qApp.quit) def openFileFromMenu(self): options = QFileDialog.Options() filenames, _ = QFileDialog.getOpenFileNames( self, 'Open a file', '', 'All Files (*);;Python Files (*.py);;Text Files (*.txt)', options=options) if filenames: # If file is selected, we can open it filename = filenames[0] if filename[-3:] in ['gif', 'png', 'jpg', 'bmp' ] or filename[-4:] in ['jpeg']: self.pic_opened = True self.openFile(filename) def openBrowser(self, url, word): widget = Browser(url) index = self.tab.tabs.addTab(widget, "Info about: " + str(word)) self.tab.tabs.setCurrentIndex(index) def openFile(self, filename): try: for index, tabName in enumerate(self.tab.tabCounter): with open(filename, 'r+') as file_o: if filename[-3:] in ['gif', 'png', 'jpg', 'bmp' ] or filename[-4:] in ['jpeg']: self.pic_opened = True else: self.pic_opened = False try: text = file_o.read() except UnicodeDecodeError as E: text = str(E) basename = os.path.basename(filename) if not self.pic_opened: tab = Content(text, filename, basename, self, ex) tab.saved = True tab.modified = False else: tab = Image(filename, basename) if tabName == tab.baseName: self.tab.tabs.removeTab(index) self.tab.tabCounter.remove(tab.baseName) try: with open(filename, 'r+') as file_o: try: if self.pic_opened is not True: text = file_o.read() else: text = None except (FileNotFoundError, UnicodeDecodeError, AttributeError) as E: text = str(E) except FileNotFoundError: with open(filename, 'w+') as newFileCreated: text = newFileCreated.read() basename = os.path.basename(filename) if self.pic_opened is True: tab = Image(filename, basename) else: tab = Content(text, filename, basename, self, ex) # Creating a tab object *IMPORTANT* tab.saved = True tab.modified = False self.tab.tabCounter.append(tab.baseName) dirPath = os.path.dirname(filename) self.files = filename self.tabsOpen.append(self.files) index = self.tab.tabs.addTab( tab, tab.baseName ) # This is the index which we will use to set the current self.tab.tabs.setTabToolTip(index, str(tab.fileName)) if not self.dir_opened: # If a project isnt opened then we open a directory everytime we open a file self.tab.directory.openDirectory(dirPath) self.tab.showDirectory() else: pass self.tab.setLayout(self.tab.layout) # Finally we set the layout self.tab.tabs.setCurrentIndex( index) # Setting the index so we could find the current widget self.currentTab = self.tab.tabs.currentWidget() if self.pic_opened is not True: self.currentTab.editor.setFont(self.font) # Setting the font self.currentTab.editor.setFocus( ) # Setting focus to the tab after we open it self.pic_opened = False except (IsADirectoryError, AttributeError, UnboundLocalError, PermissionError) as E: print(E, " on line 346 in the file main.py") def newFile(self): text = "" if self._dir: base_file_name = "Untitled_file_" + str(random.randint( 1, 100)) + ".py" fileName = str(self._dir) + "/" + base_file_name else: base_file_name = "Untitled_file_" + str(random.randint( 1, 100)) + ".py" current = os.getcwd() fileName = current + "/" + base_file_name self.pyFileOpened = True # Creates a new blank file file = Content(text, fileName, base_file_name, self, ex) self.tab.splitterH.addWidget( self.tab.tabs ) # Adding tabs, now the directory tree will be on the left self.tab.tabCounter.append(file.fileName) self.tab.setLayout(self.tab.layout) # Finally we set the layout index = self.tab.tabs.addTab( file, file.baseName ) # addTab method returns an index for the tab that was added self.tab.tabs.setTabToolTip(index, str(file.fileName)) self.tab.tabs.setCurrentIndex( index) # Setting focus to the new tab that we created widget = self.tab.tabs.currentWidget() def newProjectFolder(self): self.dialog = MessageBox() self.dialog.newProject() def openProject(self): self._dir = QFileDialog.getExistingDirectory(None, 'Select a folder:', '', QFileDialog.ShowDirsOnly) self.tab.directory.openDirectory(self._dir) self.dir_opened = True self.tab.showDirectory() def saveFile(self): try: active_tab = self.tab.tabs.currentWidget() if self.tab.tabs.count(): # If a file is already opened with open(active_tab.fileName, 'w+') as saveFile: saveFile.write(active_tab.editor.text()) active_tab.saved = True self.tab.events.look_for_dead_code( active_tab.editor.text()) active_tab.modified = False saveFile.close() if active_tab.fileName.endswith(".py"): active_tab.editor.updateAutoComplete(active_tab.fileName) else: options = QFileDialog.Options() name = QFileDialog.getSaveFileName( self, 'Save File', '', 'All Files (*);;Python Files (*.py);;Text Files (*.txt)', options=options) fileName = name[0] with open(fileName, "w+") as saveFile: active_tab.saved = True active_tab.modified = False self.tabsOpen.append(fileName) saveFile.write(active_tab.editor.text()) self.tab.events.look_for_dead_code( active_tab.editor.text()) saveFile.close() if fileName.endswith(".py"): active_tab.editor.updateAutoComplete( active_tab.fileName) ex.setWindowTitle("PyPad ~ " + str(active_tab.baseName) + " [SAVED]") active_tab.tokenize_file() except Exception as E: print(E, " on line 403 in the file main.py") def choose_python(self): if self.os == "Windows": return "python" elif self.os == "Linux": return "python3" elif self.os == "Darwin": return "python3" def saveFileAs(self): try: active_tab = self.tab.tabs.currentWidget() if active_tab is not None: active_index = self.tab.tabs.currentIndex() options = QFileDialog.Options() name = QFileDialog.getSaveFileName( self, 'Save File', '', 'All Files (*);;Python Files (*.py);;Text Files (*.txt)', options=options) fileName = name[0] with open(fileName, "w+") as saveFile: active_tab.saved = True active_tab.modified = False self.tabsOpen.append(fileName) try: baseName = os.path.basename(fileName) except AttributeError: print("All tabs closed") saveFile.write(active_tab.editor.text()) text = active_tab.editor.text() newTab = Content(str(text), fileName, baseName, self, ex) self.tab.tabs.removeTab( active_index ) # When user changes the tab name we make sure we delete the old one index = self.tab.tabs.addTab( newTab, newTab.baseName) # And add the new one! self.tab.tabs.setTabToolTip(index, str(newTab.fileName)) self.tab.tabs.setCurrentIndex(index) newActiveTab = self.tab.tabs.currentWidget() newActiveTab.editor.setFont(self.font) newActiveTab.editor.setFocus() saveFile.close() ex.setWindowTitle("PyPad ~ " + str(active_tab.baseName) + " [SAVED]") else: print("No file opened") except FileNotFoundError: print("File dialog closed") def realterminal(self): """ Checking if the file executing widget already exists in the splitter layout: If it does exist, then we're going to replace the widget with the terminal widget, if it doesn't exist then just add the terminal widget to the layout and expand the splitter. """ if self.tab.splitterV.indexOf(self.tab.Console) == 1: self.tab.splitterV.replaceWidget( self.tab.splitterV.indexOf(self.tab.Console), self.tab.terminal) self.tab.splitterV.setSizes([400, 10]) else: self.tab.showConsole() def execute_file(self): """ Checking if the terminal widget already exists in the splitter layout: If it does exist, then we're going to replace it, if it doesn't then we're just gonna add our file executer to the layout, expand the splitter and run the file. Then check if the file executer already exists, but is called again to run the file again """ active_tab = self.tab.tabs.currentWidget() python_command = self.choose_python() if self.tab.splitterV.indexOf(self.tab.terminal) == 1: self.tab.splitterV.replaceWidget( self.tab.splitterV.indexOf(self.tab.terminal), self.tab.Console) self.tab.Console.run( "{} ".format(python_command) + active_tab.fileName, active_tab.fileName) self.tab.splitterV.setSizes([400, 10]) elif self.tab.splitterV.indexOf(self.tab.Console) == 1: self.tab.Console.run( "{} ".format(python_command) + active_tab.fileName, active_tab.fileName) else: self.tab.showFileExecuter() self.tab.Console.run( "{} ".format(python_command) + active_tab.fileName, active_tab.fileName)
class Tabs(QWidget): def __init__(self, callback, app, palette, parent=None): super().__init__() self.app = app self.parent = parent self.palette = palette self.terminal = Terminal(self, False) self.tool_layout = QVBoxLayout() self.tool_layout_bar = QHBoxLayout() self.layout = QVBoxLayout(self) # Initialize tab screen self.tabs = QTabWidget() self.events = Events() self.tabs.setStyleSheet(""" QTabWidget::pane { /* The tab widget frame */ border-top: 0.5px solid #2c2c2c; } QTabWidget::tab-bar { } /* Style the tab using the tab sub-control. Note that it reads QTabBar _not_ QTabWidget */ QTabBar::tab { background: #212121; border-bottom: 2px solid #303030; border-bottom-color: #434343; min-width: 8ex; margin-left: 10px; padding-left: 25px; padding-right: 20px; padding-top: 3px; padding-bottom: 3px; } QTabBar::tab:selected, QTabBar::tab:hover { background: #212121; } QToolTip { padding: 3px; font-family: \"Iosevka\"; font-size: 14px; color: #FFFFFF; background: #2c2c2c; } QTabBar::tab:selected { border-bottom-color: #FFFFFF; /* same as pane color */ } """) font = QFont(editor['tabFont']) font.setPointSize( editor["tabFontSize"]) # This is the tab font and font size self.tabs.setFont(font) self.status = QStatusBar(self) self.dialog = MessageBox(self) self.tabs.usesScrollButtons() self.filelist = [] self.tabSaved = False self.Console = Console( self) # This is the terminal widget and the SECOND thread self.directory = Directory(callback, self.app, self.palette) # TODO: This is top left self.directory.clearSelection() self.tabCounter = [] # Add tabs self.tab_layout = QHBoxLayout( ) # Create new layout for original tab layout self.tab_layout.addWidget(self.tabs) # Add tab widget to tab layout self.search_layout = QHBoxLayout() self.tabs.setTabsClosable(True) self.tabs.setMovable( editor['tabMovable']) # Let's you make the tabs movable if editor[ 'tabShape'] is True: # If tab shape is true then they have this rounded look self.tabs.setTabShape(1) else: self.tabs.setTabShape(0) # If false, it has this boxy look self.tabs.tabCloseRequested.connect(self.closeTab) # Build Layout self.layout.addLayout( self.tab_layout) # Adds 'TOP' layout : tab + directory self.layout.addLayout(self.search_layout) # Creating horizontal splitter self.splitterH = QSplitter(Qt.Horizontal) # Creating vertical splitter self.splitterV = QSplitter(Qt.Vertical) self.splitterV2 = QSplitter(Qt.Vertical) self.splitterV.addWidget(self.splitterH) self.layout.addWidget(self.splitterV) self.splitterV.setSizes([400, 10]) self.setLayout(self.layout) # Sets layout of QWidget self.closeShortcut = QShortcut( QKeySequence(editor["closeTabShortcut"]), self) self.closeShortcut.activated.connect(self.closeTabShortcut) self.getAllOpenTabs = QShortcut(QKeySequence("Ctrl+Shift+W"), self) self.getAllOpenTabs.activated.connect(self.getAllOpenTabsFunc) currentTab = self.tabs.currentWidget() self.layout.addLayout(self.tool_layout) self.layout.addWidget(self.splitterV) self.hideDirectory() @pyqtSlot() def closeTabShortcut(self): self.index = self.tabs.currentIndex() self.closeTab(self.index) def getAllOpenTabsFunc(self): word = 'import' for tab in range(self.tabs.count()): file = self.tabs.widget(tab).fileName if file not in self.filelist: self.filelist.append(file) for file in self.filelist: openedFileContents = open(file, 'r').read() def closeTab(self, index): try: tab = self.tabs.widget(index) if tab.saved is True and tab.modified is False: tab.deleteLater() self.tabCounter.pop(index) self.filelist.pop(index) self.tabs.removeTab(index) elif tab.modified is True: self.dialog.saveMaybe(tab, self.tabCounter, self.tabs, index) except (AttributeError, IndexError) as E: try: tab.deleteLater() self.tabCounter.pop(index) self.filelist.pop(index) self.tabs.removeTab(index) except (AttributeError, IndexError) as E: print(E, " on line 175 in the file Tabs.py") def showDirectory(self): self.directory.setVisible(True) self.tab_layout.removeWidget(self.tabs) self.splitterV2.addWidget(self.directory) self.splitterV2.addWidget(self.events) self.splitterH.addWidget(self.splitterV2) self.splitterH.addWidget( self.tabs ) # Adding tabs, now the directory tree will be on the left def hideDirectory(self): self.tab_layout.removeWidget(self.directory) self.directory.setVisible(False) """ Because the root layouts are set all you have to do now is just add/remove widgets from the parent layout associated This keeps the UI order set as intended as built above when initialized. """ def hideConsole(self): self.splitterV.setSizes([0, 0]) def showConsole(self): self.splitterV.addWidget(self.terminal) self.splitterV.setSizes([400, 10]) self.terminal.clicked = False def hideFileExecuter(self): self.splitterV.setSizes([0, 0]) def showFileExecuter(self): self.splitterV.addWidget(self.Console) self.splitterV.setSizes([400, 10]) self.Console.clicked = False def currentTab(self): return self.tabs.currentWidget()
class Editor(QPlainTextEdit): def __init__(self, parent): super().__init__(parent) self.lineNumberArea = QLineNumberArea(self) self.blockCountChanged.connect(self.updateLineNumberAreaWidth) self.updateRequest.connect(self.updateLineNumberArea) self.textChanged.connect(self.check) self.cursorPositionChanged.connect(self.highlightCurrentLine) self.updateLineNumberAreaWidth(0) self.parent = parent self.font = QFont() self.size = 12 self.dialog = MessageBox() self.menu_font = QFont() self.menu_font.setFamily("Iosevka") self.menu_font.setPointSize(10) self.font.setFamily(editor["editorFont"]) self.font.setPointSize(editor["editorFontSize"]) self.focused = None self.replace_tabs = 4 self.setWordWrapMode(4) self.setFont(self.font) self.l = 0 self.highlightingRules = [] self.indexes = None self.setTabStopWidth(editor["TabWidth"]) self.createStandardContextMenu() self.setWordWrapMode(QTextOption.NoWrap) def get_linenumbers(self): return self.blockCount() def check(self): cursor = self.textCursor() b = cursor.block() if len(b.text()) >= 79: print("pep 8 violation on line: " + str(b.blockNumber() + 1)) def newFile(self): """This and most of the functions below will just be wrappers for the functions defined in Main""" self.new_action = QAction("New") self.new_action.triggered.connect(self.parent.parent.newFile) def lineNumberAreaWidth(self): digits = 1 max_value = max(1, self.blockCount()) while max_value >= 10: max_value /= 10 digits += 1 space = 10 + self.fontMetrics().width('9') * digits return space def updateLineNumberAreaWidth(self, _): self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0) def updateLineNumberArea(self, rect, dy): if dy: self.lineNumberArea.scroll(0, dy) else: self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height()) if rect.contains(self.viewport().rect()): self.updateLineNumberAreaWidth(0) def resizeEvent(self, event): super().resizeEvent(event) cr = self.contentsRect() self.lineNumberArea.setGeometry( QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height())) def highlightCurrentLine(self): extraSelections = [] if not self.isReadOnly(): selection = QTextEdit.ExtraSelection() lineColor = QColor("#434343") selection.format.setBackground(lineColor) selection.format.setProperty(QTextFormat.FullWidthSelection, True) selection.cursor = self.textCursor() selection.cursor.clearSelection() extraSelections.append(selection) self.setExtraSelections(extraSelections) def lineNumberAreaPaintEvent(self, event): painter = QPainter(self.lineNumberArea) painter.fillRect(event.rect(), QColor("#303030")) block = self.firstVisibleBlock() blockNumber = block.blockNumber() top = self.blockBoundingGeometry(block).translated( self.contentOffset()).top() bottom = top + self.blockBoundingRect(block).height() # Just to make sure I use the right font height = self.fontMetrics().height() while block.isValid() and (top <= event.rect().bottom()): if block.isVisible() and (bottom >= event.rect().top()): number = str(blockNumber + 1) painter.setPen(Qt.white) painter.drawText(0, top, self.lineNumberArea.width(), height, Qt.AlignCenter, number) block = block.next() top = bottom bottom = top + self.blockBoundingRect(block).height() blockNumber += 1 def openFile(self): self.open_action = QAction("Open") self.open_action.triggered.connect(self.parent.parent.openFileFromMenu) def runFile(self): self.run_action = QAction("Run") self.run_action.triggered.connect(self.parent.parent.terminal) def contextMenuEvent(self, event): menu = QMenu() """Initializing actions""" self.newFile() self.openFile() self.runFile() print("aa") menu.addAction(self.new_action) menu.addAction(self.open_action) menu.addAction(self.run_action) menu.setFont(self.menu_font) menu.exec(event.globalPos()) del menu def moveCursorPosBack(self): textCursor = self.textCursor() textCursorPos = textCursor.position() textCursor.setPosition(textCursorPos - 1) self.setTextCursor(textCursor) def keyPressEvent(self, e): textCursor = self.textCursor() key = e.key() if key == Qt.Key_H: # self.parent.completer.wordList # TODO: implement dynamic completion pass textCursorPos = textCursor.position() isSearch = (e.modifiers() == Qt.ControlModifier and e.key() == Qt.Key_F) if isSearch: try: currentWidget = self.parent currentFile = currentWidget.fileName currentEditor = currentWidget.editor textCursor = currentEditor.textCursor() textCursorPos = textCursor.position() except (AttributeError, UnboundLocalError) as E: print(E) if currentWidget is not None: text, okPressed = QInputDialog.getText(self, 'Find', 'Find what: ') if okPressed: if text == "": text = " " self.dialog.noMatch(text) self.searchtext = text try: with open(currentFile, 'r') as file: contents = file.read() self.indexes = list(find_all(contents, text)) if len(self.indexes) == 0: self.dialog.noMatch(text) except FileNotFoundError as E: print(E) if key == Qt.Key_QuoteDbl: self.insertPlainText('"') self.moveCursorPosBack() if e.modifiers() == Qt.ControlModifier and e.key( ) == 61: # Press Ctrl+Equal key to make font bigger self.font.setPointSize(self.size + 1) self.font.setFamily(editor["editorFont"]) self.setFont(self.font) self.size += 1 if e.modifiers() == Qt.ControlModifier and e.key() == 16777217: return if e.modifiers() == Qt.ControlModifier and e.key( ) == 45: # Press Ctrl+Minus key to make font smaller self.font.setPointSize(self.size - 1) self.font.setFamily(editor["editorFont"]) self.setFont(self.font) self.size -= 1 if key == Qt.Key_F3: try: index = self.indexes[0 + self.l] currentWidget = self.parent currentFile = currentWidget.fileName currentEditor = currentWidget.editor textCursor.setPosition(index) textCursor.movePosition(textCursor.Right, textCursor.KeepAnchor, len(self.searchtext)) currentEditor.setTextCursor(textCursor) self.l += 1 except IndexError: self.l = 0 if key == 39: self.insertPlainText("'") self.moveCursorPosBack() if key == Qt.Key_BraceLeft: self.insertPlainText("}") self.moveCursorPosBack() if key == Qt.Key_BracketLeft: self.insertPlainText("]") self.moveCursorPosBack() if key == Qt.Key_ParenLeft: self.insertPlainText(")") self.moveCursorPosBack() if key == Qt.Key_ParenRight: textCursor = self.textCursor() textCursor.select(QTextCursor.WordUnderCursor) if textCursor.selectedText( ) == "()" or "()" in textCursor.selectedText(): return if key == Qt.Key_BraceRight: textCursor = self.textCursor() textCursor.select(QTextCursor.WordUnderCursor) if textCursor.selectedText == "": return if key not in [16777217, 16777219, 16777220]: super().keyPressEvent(e) return e.accept() cursor = self.textCursor() if key == 16777217: # and self.replace_tabs: amount = 4 - self.textCursor().positionInBlock() % 4 self.insertPlainText(' ' * amount) elif key == 16777219 and cursor.selectionStart() == cursor.selectionEnd() and self.replace_tabs and \ cursor.positionInBlock(): position = cursor.positionInBlock() end = cursor.position() start = end - (position % 4) if start == end and position >= 4: start -= 4 string = self.toPlainText()[start:end] if not len(string.strip() ): # if length is 0 which is binary for false for i in range(end - start): cursor.deletePreviousChar() else: super().keyPressEvent(e) elif key == 16777220: end = cursor.position() start = end - cursor.positionInBlock() line = self.toPlainText()[start:end] indentation = len(line) - len(line.lstrip()) chars = '\t' if self.replace_tabs: chars = ' ' indentation /= self.replace_tabs if line.endswith(':'): if self.replace_tabs: indentation += 1 super().keyPressEvent(e) self.insertPlainText(chars * int(indentation)) else: super().keyPressEvent(e)
def __init__(self, callback, app, palette, parent=None): super().__init__() self.app = app self.parent = parent self.palette = palette self.terminal = Terminal(self, False) self.tool_layout = QVBoxLayout() self.tool_layout_bar = QHBoxLayout() self.layout = QVBoxLayout(self) # Initialize tab screen self.tabs = QTabWidget() self.events = Events() self.tabs.setStyleSheet(""" QTabWidget::pane { /* The tab widget frame */ border-top: 0.5px solid #2c2c2c; } QTabWidget::tab-bar { } /* Style the tab using the tab sub-control. Note that it reads QTabBar _not_ QTabWidget */ QTabBar::tab { background: #212121; border-bottom: 2px solid #303030; border-bottom-color: #434343; min-width: 8ex; margin-left: 10px; padding-left: 25px; padding-right: 20px; padding-top: 3px; padding-bottom: 3px; } QTabBar::tab:selected, QTabBar::tab:hover { background: #212121; } QToolTip { padding: 3px; font-family: \"Iosevka\"; font-size: 14px; color: #FFFFFF; background: #2c2c2c; } QTabBar::tab:selected { border-bottom-color: #FFFFFF; /* same as pane color */ } """) font = QFont(editor['tabFont']) font.setPointSize( editor["tabFontSize"]) # This is the tab font and font size self.tabs.setFont(font) self.status = QStatusBar(self) self.dialog = MessageBox(self) self.tabs.usesScrollButtons() self.filelist = [] self.tabSaved = False self.Console = Console( self) # This is the terminal widget and the SECOND thread self.directory = Directory(callback, self.app, self.palette) # TODO: This is top left self.directory.clearSelection() self.tabCounter = [] # Add tabs self.tab_layout = QHBoxLayout( ) # Create new layout for original tab layout self.tab_layout.addWidget(self.tabs) # Add tab widget to tab layout self.search_layout = QHBoxLayout() self.tabs.setTabsClosable(True) self.tabs.setMovable( editor['tabMovable']) # Let's you make the tabs movable if editor[ 'tabShape'] is True: # If tab shape is true then they have this rounded look self.tabs.setTabShape(1) else: self.tabs.setTabShape(0) # If false, it has this boxy look self.tabs.tabCloseRequested.connect(self.closeTab) # Build Layout self.layout.addLayout( self.tab_layout) # Adds 'TOP' layout : tab + directory self.layout.addLayout(self.search_layout) # Creating horizontal splitter self.splitterH = QSplitter(Qt.Horizontal) # Creating vertical splitter self.splitterV = QSplitter(Qt.Vertical) self.splitterV2 = QSplitter(Qt.Vertical) self.splitterV.addWidget(self.splitterH) self.layout.addWidget(self.splitterV) self.splitterV.setSizes([400, 10]) self.setLayout(self.layout) # Sets layout of QWidget self.closeShortcut = QShortcut( QKeySequence(editor["closeTabShortcut"]), self) self.closeShortcut.activated.connect(self.closeTabShortcut) self.getAllOpenTabs = QShortcut(QKeySequence("Ctrl+Shift+W"), self) self.getAllOpenTabs.activated.connect(self.getAllOpenTabsFunc) currentTab = self.tabs.currentWidget() self.layout.addLayout(self.tool_layout) self.layout.addWidget(self.splitterV) self.hideDirectory()
class Editor(QPlainTextEdit): def __init__(self, parent): super().__init__(parent) self.parent = parent self.font = QFont() self.size = 12 self.dialog = MessageBox() self.menu_font = QFont() self.menu_font.setFamily("Iosevka") self.menu_font.setPointSize(10) self.font.setFamily(editor["editorFont"]) self.font.setPointSize(editor["editorFontSize"]) self.focused = None self.replace_tabs = 4 self.setWordWrapMode(4) self.setFont(self.font) self.l = 0 self.highlightingRules = [] self.indexes = None self.setTabStopWidth(editor["TabWidth"]) self.createStandardContextMenu() self.setWordWrapMode(QTextOption.NoWrap) def newFile(self): """This and most of the functions below will just be wrappers for the functions defined in Main""" self.new_action = QAction("New") self.new_action.triggered.connect(self.parent.parent.newFile) def openFile(self): self.open_action = QAction("Open") self.open_action.triggered.connect(self.parent.parent.openFileFromMenu) def runFile(self): self.run_action = QAction("Run") self.run_action.triggered.connect(self.parent.parent.Terminal) def contextMenuEvent(self, event): menu = QMenu() """Initializing actions""" self.newFile() self.openFile() self.runFile() menu.addAction(self.new_action) menu.addAction(self.open_action) menu.addAction(self.run_action) menu.setFont(self.menu_font) menu.exec(event.globalPos()) del menu def moveCursorPosBack(self): textCursor = self.textCursor() textCursorPos = textCursor.position() textCursor.setPosition(textCursorPos - 1) self.setTextCursor(textCursor) def keyPressEvent(self, e): textCursor = self.textCursor() key = e.key() if key == Qt.Key_H: # self.parent.completer.wordList # TODO: implement dynamic completion pass textCursorPos = textCursor.position() isSearch = (e.modifiers() == Qt.ControlModifier and e.key() == Qt.Key_F) if isSearch: try: currentWidget = self.parent currentFile = currentWidget.fileName currentEditor = currentWidget.editor textCursor = currentEditor.textCursor() textCursorPos = textCursor.position() except (AttributeError, UnboundLocalError) as E: print(E) if currentWidget is not None: text, okPressed = QInputDialog.getText(self, 'Find', 'Find what: ') if okPressed: if text == "": text = " " self.dialog.noMatch(text) self.searchtext = text try: with open(currentFile, 'r') as file: contents = file.read() self.indexes = list(find_all(contents, text)) if len(self.indexes) == 0: self.dialog.noMatch(text) except FileNotFoundError as E: print(E) if key == Qt.Key_QuoteDbl: self.insertPlainText('"') self.moveCursorPosBack() if (e.modifiers() == Qt.ControlModifier and e.key() == 61): # Press Ctrl+Equal key to make font bigger self.font.setPointSize(self.size + 1) self.font.setFamily(editor["editorFont"]) self.setFont(self.font) self.size += 1 if (e.modifiers() == Qt.ControlModifier and e.key() == 45): # Press Ctrl+Minus key to make font smaller self.font.setPointSize(self.size - 1) self.font.setFamily(editor["editorFont"]) self.setFont(self.font) self.size -= 1 if key == Qt.Key_F3: try: index = self.indexes[0 + self.l] currentWidget = self.parent currentFile = currentWidget.fileName currentEditor = currentWidget.editor textCursor.setPosition(index) textCursor.movePosition(textCursor.Right, textCursor.KeepAnchor, len(self.searchtext)) currentEditor.setTextCursor(textCursor) self.l += 1 except IndexError: self.l = 0 if key == 39: self.insertPlainText("'") self.moveCursorPosBack() if key == Qt.Key_BraceLeft: self.insertPlainText("}") self.moveCursorPosBack() if key == Qt.Key_BracketLeft: self.insertPlainText("]") self.moveCursorPosBack() if key == Qt.Key_ParenLeft: self.insertPlainText(")") self.moveCursorPosBack() if key == Qt.Key_ParenRight: textCursor = self.textCursor() textCursor.select(QTextCursor.WordUnderCursor) if textCursor.selectedText() == "()" or "()" in textCursor.selectedText(): return if key == Qt.Key_BraceRight: textCursor = self.textCursor() textCursor.select(QTextCursor.WordUnderCursor) if textCursor.selectedText == "": return if key not in [16777217, 16777219, 16777220]: super().keyPressEvent(e) return e.accept() cursor = self.textCursor() if key == 16777217: # and self.replace_tabs: amount = 4 - self.textCursor().positionInBlock() % 4 self.insertPlainText(' ' * amount) elif key == 16777219 and cursor.selectionStart() == cursor.selectionEnd() and self.replace_tabs and \ cursor.positionInBlock(): position = cursor.positionInBlock() end = cursor.position() start = end - (position % 4) if start == end and position >= 4: start -= 4 string = self.toPlainText()[start:end] if not len(string.strip()): # if length is 0 which is binary for false for i in range(end - start): cursor.deletePreviousChar() else: super().keyPressEvent(e) elif key == 16777220: end = cursor.position() start = end - cursor.positionInBlock() line = self.toPlainText()[start:end] indentation = len(line) - len(line.lstrip()) chars = '\t' if self.replace_tabs: chars = ' ' indentation /= self.replace_tabs if line.endswith(':'): if self.replace_tabs: indentation += 1 super().keyPressEvent(e) self.insertPlainText(chars * int(indentation)) else: super().keyPressEvent(e)
def newProjectFolder(self): self.dialog = MessageBox() self.dialog.newProject()
class Editor(QsciScintilla): def __init__(self, parent=None): super().__init__(parent) self.fileName = None self.parent = parent self.debugging = False self.line = None self.column = None self.wordlist = [] self.searchtext = None self.font = QFont() self.font.setFamily("Inconsolata") self.pointSize = 12 # TODO: Make this customizable self.tabWidth = 4 # TODO: Make this customizable self.font.setPointSize(self.pointSize) self.dialog = MessageBox(self) self.verticalScrollBar().setStyleSheet(""" background-color: transparent; """) self.horizontalScrollBar().setStyleSheet(""" background-color: transparent; """) self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCaretForegroundColor(QColor("#FFFFFF")) self.setEdgeColumn(121) self.setEdgeMode(1) self.setEdgeColor(QColor("#8c8c8c")) self.setFont(self.font) self.setMarginSensitivity(1, True) self.markerDefine(QsciScintilla.RightArrow, 8) self.setMarkerBackgroundColor(QColor('#FF0000'), 8) self.indicator_number = 0 self.indicator_value = 222 self.indicator_color = QColor("#FF0000") self.draw_under_text = True # Initializing some stuff self.set_brace_colors(QColor("#98b4f9"), QColor("#edf40e"), QColor("#98b4f9"), QColor("red")) self.cursorPositionChanged.connect(self.change_col) self.textChanged.connect(self.check_lines) self.set_linenumbers(QFontMetrics(self.font)) self.setFoldMarginColors(QColor("#212121"), QColor("#212121")) self.set_indentation_settings(self.tabWidth) def set_up_tooltips(self): self.setCallTipsStyle(QsciScintilla.CallTipsNoContext) self.setCallTipsVisible(0) self.setCallTipsPosition(QsciScintilla.CallTipsAboveText) self.setCallTipsBackgroundColor(QColor("#FF0000")) self.setCallTipsForegroundColor(QColor("#FF0000")) self.setCallTipsHighlightColor(QColor("#FF0000")) def set_brace_colors(self, matched_B=None, matched_F=None, unmatched_B=None, unmatched_F=None): self.setMatchedBraceBackgroundColor(matched_B) self.setMatchedBraceForegroundColor(matched_F) self.setUnmatchedBraceBackgroundColor(unmatched_B) self.setUnmatchedBraceForegroundColor(unmatched_F) self.setBraceMatching(QsciScintilla.SloppyBraceMatch) def set_linenumbers(self, fontmetrics): self.setMarginsFont(self.font) self.setMarginWidth(0, fontmetrics.width("00000")) self.setMarginLineNumbers(0, True) self.setMarginsBackgroundColor(QColor("#212121")) self.setMarginsForegroundColor(QColor("#FFFFFF")) def set_indentation_settings(self, tab_width): self.setIndentationsUseTabs(False) self.setTabWidth(tab_width) self.SendScintilla(QsciScintilla.SCI_SETUSETABS, False) self.setAutoIndent(True) self.setTabIndents(True) def check_lines(self): line_n = self.lines() for i in range(line_n): if self.lineLength(i) > 121: # TODO: Make a character format or something pass # print("Line over 121 characters on line", str(i+1)) # self.setCursorPosition(i, 120) def python_highlighter(self): self.lexer = PythonLexer() self.lexer.setFoldComments(True) self.setCaretLineVisible(True) self.setDefaultSettings(self.lexer) self.setPythonAutocomplete() self.setFold() def json_highlighter(self): lexer = QsciLexerJSON() self.setDefaultSettings(lexer) def c_highlighter(self): lexer = QsciLexerCPP() self.setDefaultSettings(lexer) def setDefaultSettings(self, lexer): self.setAutoIndent(True) lexer.setFont(self.font) lexer.setColor(QColor('white'), 0) # default lexer.setColor(QColor('#6B6E6C'), PythonLexer.Comment) # = 1 lexer.setColor(QColor('#ADD4FF'), 2) # Number = 2 lexer.setColor(QColor('#38ef7d'), 3) # DoubleQuotedString lexer.setColor(QColor('#38ef7d'), 4) # SingleQuotedString lexer.setColor(QColor('#F6DC74'), 5) # Keyword lexer.setColor(QColor('#38ef7d'), 6) # TripleSingleQuotedString lexer.setColor(QColor('#38ef7d'), 7) # TripleDoubleQuotedString lexer.setColor(QColor('#74F6C3'), 8) # ClassName lexer.setColor(QColor('#FF6666'), 9) # FunctionMethodName lexer.setColor(QColor('magenta'), 10) # Operator lexer.setColor(QColor('white'), 11) # Identifier lexer.setColor(QColor('gray'), 12) # CommentBlock lexer.setColor(QColor('#a8ff78'), 13) # UnclosedString lexer.setColor(QColor('gray'), 14) # HighlightedIdentifier lexer.setColor(QColor('#FF00E7'), 15) # Decorator lexer.setFont(QFont("Iosevka", weight=QFont.Bold), 5) self.setCaretLineBackgroundColor(QColor("#3C3B3F")) self.setLexer(lexer) def setPythonAutocomplete(self): self.autocomplete = QsciAPIs(self.lexer) self.keywords = wordList for word in self.keywords: self.autocomplete.add(word) self.setAutoCompletionThreshold(2) self.setAutoCompletionSource(QsciScintilla.AcsAPIs) self.updateAutoComplete(self.parent.fileName) self.autocomplete.prepare() def setFold(self): # setup Fold Styles for classes and functions ... x = self.FoldStyle(self.FoldStyle(5)) # self.textPad.folding() if not x: self.foldAll(False) self.setFolding(x) # self.textPad.folding() def unsetFold(self): self.setFolding(0) def updateAutoComplete(self, file_path=None): for i in tokenize(file_path): for j in i: if j not in self.wordlist: self.wordlist.append(j) for word in self.wordlist: self.autocomplete.add(word) self.autocomplete.prepare() def change_col(self, line, column): # Responsible for changing the column bar. self.line = line self.column = column def check_if_func(self, word): # Checks if a word is a built in function word_array = list(word) for wo in word_array: if wo in ["{", "}", "'", '"', "[", "]", "(", ")"]: word_array.remove(wo) for w in funcList: if w == "".join(word_array): return True def check_if_error(self, word): if word in errorList: # This is the list where all possible errors are defined return True def keyReleaseEvent(self, e): if e.key() == Qt.Key_Return: try: self.updateAutoComplete(self.parent.fileName) except AttributeError as E: print(E, "on line 210 in TextEditor.py") if e.key() == Qt.Key_Backspace: pass def mousePressEvent(self, e): super().mousePressEvent(e) if QGuiApplication.queryKeyboardModifiers() == Qt.ControlModifier: word = self.wordAtLineIndex(self.getCursorPosition()[0], self.getCursorPosition()[1]) print(word) if self.check_if_func(word): url = "https://docs.python.org/3/library/functions.html#" + word self.parent.parent.openBrowser( url, word) # Runs the openBrowser function in Main class elif self.check_if_error(word): url = "https://docs.python.org/3/library/exceptions.html#" + word print(url) self.parent.parent.openBrowser(url, word) def keyPressEvent(self, e): if e.modifiers() == Qt.ControlModifier and e.key() == Qt.Key_F: text, okPressed = QInputDialog.getText(self, 'Find', 'Find what: ') self.setSelectionBackgroundColor(QColor("#6be585")) if okPressed: if text == "": text = " " self.dialog.noMatch(text) self.searchtext = text """ This is the way to implement a search function using QScintilla http://pyqt.sourceforge.net/Docs/QScintilla2/classQsciScintilla.html#a37ac2bea94eafcfa639173557a821200 """ if self.findFirst(self.searchtext, False, True, False, True, True, -1, -1, True, False): pass else: self.dialog.noMatch(self.searchtext) if e.key() == Qt.Key_F3: self.findNext() self.setSelectionBackgroundColor(QColor("#6be585")) if e.modifiers() == Qt.ControlModifier and e.key() == Qt.Key_L: self.setCursorPosition(self.line, self.column + 1) return if e.modifiers() == Qt.ControlModifier and e.key() == 77: self.setCursorPosition(self.line + 1, self.column) return if e.modifiers() == Qt.ControlModifier and e.key() == Qt.Key_J: self.setCursorPosition(self.line, self.column - 1) if e.modifiers() == Qt.ControlModifier and e.key() == Qt.Key_I: self.setCursorPosition(self.line - 1, self.column) if e.modifiers() == Qt.ControlModifier and e.key() == Qt.Key_T: self.parent.parent.realterminal() return super().keyPressEvent(e)