class MainWin(QMainWindow, Ui_MainWindow): def __init__(self): super(MainWin,self).__init__() self.setupUi(self) self.Login.triggered.connect(self.open_login) self.RGB.triggered.connect(self.open_image_rgb) self.LoginDialog = LoginDialog() self.ImageMainWin = ImageRGB() # 设置TreeWidgets self.trees = [self.YCPGAMETREE, self.YCPCOMPTREE, self.YLANDFILETREE] for tree in self.trees: tree.setContextMenuPolicy(Qt.CustomContextMenu) tree.customContextMenuRequested.connect(self.show_context_menu) tree.header().setMinimumSectionSize(120) self.model = QFileSystemModel() self.RailID = '' self.ylands_path = '' self.rail_user_data = '' self.ycp_game_folder_path = '' self.ycp_comp_folder_path = '' self.yland_folder_path = '' self.key = OpenKey(HKEY_CURRENT_USER, r"Software\Rail\YlandsRail") _value, type = QueryValueEx(self.key, "InstallPath") if _value: self.ylands_path = _value self.rail_user_data = Path.dirname(self.ylands_path) + '\\' + 'rail_user_data\\2000108' self.YCPTAB.currentChanged.connect(self.refresh_tab_qlistwidget) self.GroupBoxTitleDict = {0: 'YCP游戏目录', 1: 'YCP组件目录', 2: 'YLAND文件目录'} self.YCPTAB.setCurrentIndex(0) print(self.OpenDirBtn.clicked) # 打开workshop 后去RailId def open_login(self): self.LoginDialog.LoginRailIDSignel.connect(self.slot_emit) self.LoginDialog.show() def open_image_rgb(self): self.ImageMainWin.show() def slot_emit(self, flag, str): # 获得 rail_id self.RailID = str self.ycp_game_folder_path = self.rail_user_data + '\\' + self.RailID + '\\cloud_storage\\files\\Share\\Games' self.ycp_comp_folder_path = self.rail_user_data + '\\' + self.RailID + '\\cloud_storage\\files\\Share\\Compositions' self.yland_folder_path = self.rail_user_data + '\\' + self.RailID + '\\cloud_storage\\files\\Scenarios' if not Path.exists(self.ycp_game_folder_path): makedirs(self.ycp_game_folder_path) if not Path.exists(self.ycp_comp_folder_path): makedirs(self.ycp_comp_folder_path) if not Path.exists(self.yland_folder_path): makedirs(self.yland_folder_path) self.YCPTAB.setEnabled(True) self.YCPTAB.setCurrentIndex(0) self.refresh_tab_qlistwidget(0) self.setAcceptDrops(True) def refresh_tab_qlistwidget(self, index): if self.RailID != '': self.OpenDirBtn.setEnabled(True) if index == 0: self.YCPGAMETREE.setEnabled(True) self.refresh_path(index, self.ycp_game_folder_path) self.model.setRootPath(self.ycp_game_folder_path) self.YCPGAMETREE.setModel(self.model) self.YCPGAMETREE.setRootIndex(self.model.index(self.ycp_game_folder_path)) elif index == 1: self.YCPCOMPTREE.setEnabled(True) self.refresh_path(index, self.ycp_comp_folder_path) self.model.setRootPath(self.ycp_comp_folder_path) self.YCPCOMPTREE.setModel(self.model) self.YCPCOMPTREE.setRootIndex(self.model.index(self.ycp_comp_folder_path)) else: self.YLANDFILETREE.setEnabled(True) self.refresh_path(index, self.yland_folder_path) self.model.setRootPath(self.yland_folder_path) self.YLANDFILETREE.setModel(self.model) self.YLANDFILETREE.setRootIndex(self.model.index(self.yland_folder_path)) def refresh_path(self, index, path): self.files_count(path) self.frame.findChild(QGroupBox, 'groupBox').setTitle(self.GroupBoxTitleDict[index]) self.PathTxt.setText(path) if MainWin.isconnected(self.OpenDirBtn, 'clicked()'): self.OpenDirBtn.disconnect() self.OpenDirBtn.clicked.connect(lambda: self.open_dir(path, index)) #TODO 通过背景颜色,显示文件被使用的状态 def open_dir(self, path, index): # QFileDialog.getExistingDirectory(self,"浏览"+ self.GroupBoxTitleDict[index], path, QFileDialog.ShowDirsOnly) QFileDialog.getOpenFileNames(self, "浏览" + self.GroupBoxTitleDict[index], path, "All Files (*);;Text Files (*.txt)") def files_count(self, path): count = 0 for root, dirs, files in walk(path): for each in files: file = Path.splitext(each) filename, type = file if type != '.txt': count += 1 self.statusbar.showMessage("文件数:" + str(count)) @staticmethod def isconnected(obj, name): """判断信号是否连接 :param obj: 对象 :param name: 信号名,如 clicked() """ index = obj.metaObject().indexOfMethod(name) if index > -1: method = obj.metaObject().method(index) if method: return obj.isSignalConnected(method) return False def show_context_menu(self, pos): sender = self.sender() row_index = sender.indexAt(pos).row() menu = QMenu() cpy = menu.addAction('复制') cpy.triggered.connect(lambda: self.copy_selected(sender.currentIndex())) rmfile = menu.addAction('删除') rmfile.triggered.connect(lambda: self.remove_selected_file(sender.currentIndex())) menu.exec_(QCursor.pos()) def copy_selected(self, index): filename = self.model.fileName(index) filepath = self.model.filePath(index) data = QMimeData() url = QUrl.fromLocalFile(filepath) clipboard = QApplication.clipboard() data.setUrls([url]) clipboard.setMimeData(data) def remove_selected_file(self, index): filename = self.model.fileName(index) filepath = self.model.filePath(index) if not self.model.fileInfo(index).isDir(): msgBox = QMessageBox() msgBox.setText("确定删除文件:" + filename + "?") msgBox.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) ret = msgBox.exec_() if ret == QMessageBox.Ok: self.model.remove(index) else: return def dragEnterEvent(self, event): if event.mimeData().hasUrls: filetempname = event.mimeData().urls()[0].fileName() filename, extension = Path.splitext(filetempname) if self.YCPTAB.currentIndex() == 0 or self.YCPTAB.currentIndex() == 1: if extension == '.ycp': event.accept() # clipboard = QApplication.clipboard() # clipboard.clear() # clipboard.setMimeData(event.mimeData()) else: msgBox = QMessageBox() msgBox.setText("只能拖放.ycp后缀文件") ret = msgBox.exec_() event.ignore() return elif self.YCPTAB.currentIndex() == 2: if extension == '.yland': event.accept() # clipboard = QApplication.clipboard() # clipboard.clear() # clipboard.setMimeData(event.mimeData()) else: msgBox = QMessageBox() msgBox.setText("只能拖放.yland后缀文件") ret = msgBox.exec_() event.ignore() return def dragMoveEvent(self, event): if event.mimeData().hasUrls: try: event.setDropAction(Qt.CopyAction) except Exception as e: print(e) event.accept() else: event.ignore() def dropEvent(self, event): try: if event.mimeData().hasUrls: event.setDropAction(Qt.CopyAction) event.accept() filepath = event.mimeData().urls()[0] filename = filepath.fileName() if self.YCPTAB.currentIndex() == 0: self.copy_file(filepath.url().replace("file:///", ""), Path.join(self.ycp_game_folder_path, filename)) elif self.YCPTAB.currentIndex() == 1: self.copy_file(filepath.url().replace("file:///", ""), Path.join(self.ycp_comp_folder_path, filename)) else: self.copy_file(filepath.url().replace("file:///", ""), Path.join(self.yland_folder_path, filename)) else: event.ignore() except Exception as e: print(e) def copy_file(self,srcfle, dstfile): newdstfile = dstfile if not Path.isfile(srcfle): print("$%s not exist!" % (srcfle)) else: fpath, ftempname = Path.split(dstfile) if not Path.exists(fpath): makedirs(fpath) elif Path.exists(dstfile): filename, extension = Path.splitext(ftempname) newdstfile = Path.join(fpath, filename + "copybyylandsbox" + extension) copyfile(srcfle, newdstfile)
class FormWidget(QWidget): path = os.path.expanduser( '~\\Documents') # to the root directory for explorer filepath = [] # contain a list of full path of files fpath = '' # contain full path of file which we have to move,copy in explorer operation = '' # contain operation which we have to perform def __init__(self): super().__init__() self.tree = treeview( ) #created new classs inheriting QTreeView class at top of page self.model = QFileSystemModel() # self.model.setNameFilters(['TextFile(*.txt *.java *.py)']) # self.model.setNameFilterDisables(False) self.model.setRootPath('') self.tree.setModel(self.model) # self.tree.setCurrentIndex(self.model.index(r'C:\Users\user\Dropbox\PycharmProjects')) self.tree.setRootIndex(self.model.index(FormWidget.path)) self.tree.doubleClicked.connect(self.findPath) self.tree.hideColumn(1) self.tree.hideColumn(2) self.tree.hideColumn(3) # self.tree.header().hide() self.tree.setFixedWidth(200) self.tree.setContextMenuPolicy(Qt.CustomContextMenu) self.tree.customContextMenuRequested.connect(self.contextmenu) self.tab = QTabWidget() self.tab.setTabShape(1) self.tab.setTabsClosable(True) self.tab.tabCloseRequested.connect(self.closeTab) self.tab.setMovable(True) hBox = QHBoxLayout() hBox.addWidget(self.tree) hBox.addWidget(self.tab) self.setLayout(hBox) def contextmenu(self, position): path = '' index = '' menu = QMenu() new = QMenu("New") newf = QAction("New File") newd = QAction("New Directory") new.addAction(newf) new.addAction(newd) cut = QAction("Cut") copy = QAction("Copy") paste = QAction("Paste") delete = QAction("Delete") rename = QAction("Rename") explorer = QAction("Explorer") menu.addMenu(new) menu.addAction(cut) menu.addAction(copy) menu.addAction(paste) menu.addAction(delete) menu.addAction(rename) menu.addAction(explorer) try: index = self.tree.selectedIndexes()[0] path = self.sender().model().filePath(index) if os.path.isfile(path): path = os.path.dirname(path) except: path = FormWidget.path if FormWidget.operation != 'cc' and FormWidget.operation != 'c': paste.setDisabled(True) cut.setDisabled(True) copy.setDisabled(True) delete.setDisabled(True) rename.setDisabled(True) else: cut.setDisabled(True) copy.setDisabled(True) delete.setDisabled(True) rename.setDisabled(True) action = menu.exec_(self.tree.mapToGlobal(position)) if action == newf: text, ok = QInputDialog.getText(self, 'New File', 'Enter File name:') if ok: p = open(path + "/" + text, 'w') p.close() elif action == newd: text, ok = QInputDialog.getText(self, 'New Directory', 'Enter Directory name:') if ok: os.mkdir(path + "/" + text) elif action == delete: try: if self.model.isDir(index): shutil.rmtree(path) else: self.model.remove(index) except Exception as s: print(s) elif action == rename: text, ok = QInputDialog.getText( self, "Rename", "Enter new name:", text=os.path.basename(self.sender().model().filePath(index))) if ok: if self.model.isDir(index): os.rename(path, os.path.dirname(path) + "/" + text) else: os.rename(self.sender().model().filePath(index), path + '/' + text) elif action == cut: FormWidget.fpath = self.sender().model().filePath(index) FormWidget.operation = 'c' elif action == copy: FormWidget.fpath = self.sender().model().filePath(index) FormWidget.operation = 'cc' elif action == paste: if FormWidget.operation == 'c': shutil.move(FormWidget.fpath, path) elif FormWidget.operation == 'cc': if os.path.isfile(FormWidget.fpath): shutil.copy(FormWidget.fpath, path) else: shutil.copytree( FormWidget.fpath, path + "/" + os.path.basename(FormWidget.fpath)) FormWidget.operation = '' elif action == explorer: os.startfile(FormWidget.path) def findPath(self, index): path = self.sender().model().filePath(index) if os.path.isfile(path): global name FormWidget.filepath.append(path) name = os.path.basename(path) for i in range(self.tab.count()): if name == self.tab.tabText(i): self.tab.removeTab(i) break newEdit = QsciScintilla() self.checkExtensionToHighlight(path, newEdit) newEdit.setFont(QFont('Times', 10)) newEdit.setMarginType(0, QsciScintilla.NumberMargin) newEdit.setMarginWidth(0, '00000000') try: newEdit.setText(open(path).read()) newEdit.setBraceMatching(QsciScintilla.SloppyBraceMatch) newEdit.setAutoCompletionCaseSensitivity(False) newEdit.setAutoCompletionReplaceWord(False) newEdit.setAutoCompletionSource(QsciScintilla.AcsDocument) newEdit.setAutoCompletionThreshold(1) self.tab.insertTab(0, newEdit, QIcon('icon.png'), os.path.basename(path)) self.tab.setCurrentIndex(0) except: QMessageBox.warning(self, "Warning", "Unsupported file type", QMessageBox.Ok) def checkExtensionToHighlight(self, path, editor): _, extension = os.path.splitext(path) if extension == '.py': editor.setLexer(QsciLexerPython(self)) elif extension == '.html': editor.setLexer(QsciLexerHTML(self)) elif extension == '.java': editor.setLexer(QsciLexerJava(self)) elif extension == '.cs': editor.setLexer(QsciLexerCSharp(self)) elif extension == '.bat': editor.setLexer(QsciLexerBatch(self)) def closeTab(self, index): if self.tab.count() != 1: self.tab.removeTab(index) else: QMessageBox.warning(self, 'warning', "You can not close the last tab", QMessageBox.Ok, QMessageBox.Ok)
class MyMainWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.config_window() self.create_widgets() self.config_widgets() self.create_menubar() self.bind_widgets() self.show_widgets() def config_window(self): self.setWindowTitle('DirectoPy') self.setMinimumHeight(600) self.setMinimumWidth(1000) def create_widgets(self): self.central_widget = QWidget() self.main_layout = QGridLayout() self.moveup_button = QPushButton('Collapse all', self) self.goto_lineedit = QLineEdit('', self) self.goto_button = QPushButton('Go', self) self.folder_view = QTreeView(self) self.file_view = QTreeView(self) self.folder_model = QFileSystemModel(self) self.file_model = QFileSystemModel(self) def config_widgets(self): self.main_layout.addWidget(self.moveup_button, 0, 0) self.main_layout.addWidget(self.goto_lineedit, 0, 1, 1, 2) self.main_layout.addWidget(self.goto_button, 0, 3) self.main_layout.addWidget(self.folder_view, 1, 0, 1, 2) self.main_layout.addWidget(self.file_view, 1, 2, 1, 2) self.central_widget.setLayout(self.main_layout) # Кнопка "вверх" self.moveup_button.setMaximumWidth(100) # Кнопка "перейти" self.goto_button.setMaximumWidth(70) self.setCentralWidget(self.central_widget) # панели self.folder_model.setRootPath(None) self.folder_model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot) self.folder_view.setModel(self.folder_model) self.folder_view.setRootIndex(self.folder_model.index(None)) self.folder_view.clicked[QModelIndex].connect(self.clicked_onfolder) self.folder_view.hideColumn(1) self.folder_view.hideColumn(2) self.folder_view.hideColumn(3) self.file_model.setFilter(QDir.Files) self.file_view.setModel(self.file_model) self.file_model.setReadOnly(False) self.file_view.setColumnWidth(0, 200) self.file_view.setSelectionMode(QAbstractItemView.ExtendedSelection) # открытие папки при нажати на неё в окне папок def clicked_onfolder(self, index): selection_model = self.folder_view.selectionModel() index = selection_model.currentIndex() dir_path = self.folder_model.filePath(index) self.file_model.setRootPath(dir_path) self.file_view.setRootIndex(self.file_model.index(dir_path)) # ф-я открытия нового файла def open_file(self): index = self.file_view.selectedIndexes() if not index: return else: index = index[0] file_path = self.file_model.filePath(index).replace('/', '\\') print(file_path) self.file_view.update() # ф-я создания нового файла def new_file(self): global file_name index = self.folder_view.selectedIndexes() if len(index) > 0: path = self.folder_model.filePath(index[0]) for i in range(1, 9999999999999999): if not os.path.isfile(os.path.join(path, "newfile{}.txt".format(i))): file_name = os.path.join(path, "newfile{}.txt".format(i)) break file_name = os.path.abspath(file_name) open(file_name, 'w').close() else: print("Please, select folder") # ф-я удаления файла def delete_file(self): indexes = self.file_view.selectedIndexes() for i in indexes: self.file_model.remove(i) # ф-я переименования файла def rename_file(self): index = self.file_view.selectedIndexes() if not index: return else: index = index[0] self.file_view.edit(index) # ф-я копирования файла def copy_file(self): print("COPY") ask = QFileDialog.getExistingDirectory(self, "Open Directory", "C:\\", QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) new_path = ask.replace('\\', '/') indexes = self.file_view.selectedIndexes()[::4] for i in indexes: new_filename = new_path + '/' + self.file_model.fileName(i) copy2(self.file_model.filePath(i), new_filename) # ф-я возвращения к корню пути def colapse(self): self.folder_view.collapseAll() # ф-я перемещения в заданную директорию def go_to(self): dir_path = self.goto_lineedit.text().replace('\\', '/') print(dir_path) self.file_model.setRootPath(dir_path) self.file_view.setRootIndex(self.file_model.index(dir_path)) #self.file_model.setRootPath() # ф-я перемещения файла def move_file(self): print("MOVE") ask = QFileDialog.getExistingDirectory(self, "Open Directory", "C:\\", QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if ask == '': return new_path = ask.replace('\\', '/') indexes = self.file_view.selectedIndexes()[::4] for i in indexes: new_filename = new_path + '/' + self.file_model.fileName(i) move(self.file_model.filePath(i), new_filename) # ф-я создания новой папки def new_folder(self): global file_name index = self.folder_view.selectedIndexes() if len(index) > 0: path = self.folder_model.filePath(index[0]) for i in range(1, 9999999999999999): if not os.path.isdir(os.path.join(path, "newfolder{}".format(i))): file_name = os.path.join(path, "newfolder{}".format(i)) break file_name = os.path.abspath(file_name) os.mkdir(file_name) else: print("Please, select folder") # ф-я удаления папки def delete_folder(self): indexes = self.folder_view.selectedIndexes() for i in indexes: self.folder_model.remove(i) # ф-я переименования папки def rename_folder(self): index = self.folder_view.selectedIndexes() if not index: return else: index = index[0] self.folder_view.edit(index) # ф-я закрытия окна файлового менеджера def exit_application(self): print("EXIT") self.close() # задавание функции каждой кнопке def bind_widgets(self): self.open_file_action.triggered.connect(self.open_file) self.new_file_action.triggered.connect(self.new_file) self.delete_file_action.triggered.connect(self.delete_file) self.rename_file_action.triggered.connect(self.rename_file) self.copy_file_action.triggered.connect(self.copy_file) self.move_file_action.triggered.connect(self.move_file) self.exit_action.triggered.connect(self.exit_application) self.new_folder_action.triggered.connect(self.new_folder) self.delete_folder_action.triggered.connect(self.delete_folder) self.rename_folder_action.triggered.connect(self.rename_folder) self.goto_button.clicked.connect(partial(self.go_to)) self.moveup_button.clicked.connect(partial(self.colapse)) # создание меню def create_menubar(self): self.exit_action = QAction('Exit', self) self.exit_action.setShortcut('Ctrl+Q') self.new_file_action = QAction('New file', self) self.new_file_action.setShortcut('F4') self.open_file_action = QAction('Open file', self) self.open_file_action.setShortcut('F3') self.rename_file_action = QAction('Rename file', self) self.rename_file_action.setShortcut('F2') self.delete_file_action = QAction('Remove file', self) self.delete_file_action.setShortcut(QKeySequence.Delete) self.copy_file_action = QAction('Copy folder...', self) self.copy_file_action.setShortcut(QKeySequence.Copy) self.move_file_action = QAction('Move folder...', self) self.move_file_action.setShortcut(QKeySequence.Cut) self.new_folder_action = QAction('New folder', self) self.new_folder_action.setShortcut('Ctrl+Shift+N') self.delete_folder_action = QAction('Delete folder', self) self.delete_folder_action.setShortcut('Ctrl+Shift+Del') self.rename_folder_action = QAction('Rename folder', self) self.rename_folder_action.setShortcut('Ctrl+Shift+R') self.menubar = self.menuBar() self.file_menu = self.menubar.addMenu('File') self.file_menu.addAction(self.new_file_action) self.file_menu.addAction(self.open_file_action) self.file_menu.addAction(self.rename_file_action) self.file_menu.addAction(self.delete_file_action) self.file_menu.addAction(self.copy_file_action) self.file_menu.addAction(self.move_file_action) self.file_menu.addSeparator() self.file_menu.addAction(self.exit_action) self.folder_menu = self.menubar.addMenu('Folder') self.folder_menu.addAction(self.new_folder_action) self.folder_menu.addAction(self.delete_folder_action) self.folder_menu.addAction(self.rename_folder_action) def show_widgets(self): self.setLayout(self.main_layout)
class FileWidget(QWidget): def __init__(self): super().__init__() # QWidget部件是PyQt5所有用户界面对象的基类。他为QWidget提供默认构造函数。默认构造函数没有父类。 # 创建一个文件系统模型 self.file_model = QFileSystemModel() # 设置目录为当前工作目录 self.file_model.setRootPath(QDir.currentPath()) # 创建树视图,构建文件目录视图 self.treeview = QTreeView() # 绑定此文件模型 self.treeview.setModel(self.file_model) ''' 设置当前勾结点索引为当前工作目录 如果想从整个文件系统根节点开始浏览视图, 简单删掉此行即可 ''' self.treeview.setRootIndex(self.file_model.index(QDir.currentPath())) # 头部显示排序戳 self.treeview.header().setSortIndicatorShown(True) # 创建右键菜单 self.treeview.setContextMenuPolicy(Qt.CustomContextMenu) # point = self.treeview.pos() self.treeview.customContextMenuRequested.connect(self.generateMenu) ''' # 底部按钮布局 self.mkdirButton = QPushButton("Make Directory...") self.rmButton = QPushButton("Remove") buttonLayout = QHBoxLayout() buttonLayout.addWidget(self.mkdirButton) buttonLayout.addWidget(self.rmButton) ''' # 文件管理界面布局 layout = QVBoxLayout() layout.addWidget(self.treeview) # layout.addLayout(buttonLayout) # resize()方法调整窗口的大小。600px宽300px高 self.resize(600, 300) # move()方法移动窗口在屏幕上的位置到x = 300,y = 300坐标。 self.move(300, 300) # 设置窗口的标题 self.setWindowTitle('File Manage') # 设置窗口的图标 self.setWindowIcon(QIcon('File-Explorer.png')) self.setLayout(layout) # 生成右键菜单 def generateMenu(self, position): # 索引默认值 row_num = -1 # 遍历确定行号 for i in self.treeview.selectionModel().selection().indexes(): row_num = i.row() # 保证选中有效项 if row_num != -1: # 创建右键菜单 menu = QMenu() # 提供删除和创建文件/文件夹选项 item1 = menu.addAction("Delete") item2 = menu.addAction("NewDirectory") # 在光标处显示执行菜单 action = menu.exec_(self.treeview.mapToGlobal(position)) if action == item1: #弹出消息框确认此次删除操作 res = self.msgbox() if res: self.delete() else: return elif action == item2: self.mkdirectory() else: return else: return # 删除选定文件/文件夹 def delete(self): index = self.treeview.currentIndex() if index.isValid(): fileInfo = self.file_model.fileInfo(index) if fileInfo.isDir(): self.file_model.rmdir(index) else: self.file_model.remove(index) # 确认框 def msgbox(self): msgBox = QMessageBox() msgBox.setWindowTitle("Warning") msgBox.setText("Delete the file/dir you selected?") msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No) msgBox.setDefaultButton(QMessageBox.No) # button = QMessageBox.question("Warning", "delete the file/dir?", # QMessageBox.Yes | QMessageBox.No, QMessageBox.No) button = msgBox.exec_() if button == QMessageBox.No: return False elif button == QMessageBox.Yes: return True # 创建文件夹 def mkdirectory(self): index = self.treeview.rootIndex() if index.isValid(): # 弹出输入框录入文件名 dirname, ok = QInputDialog.getText(self, "File Name", "Input an unique dir name:") if ok: self.file_model.mkdir(index, dirname) else: return def keyPressEvent(self, event): if event.key() == Qt.Key_F3: self.close()