def main(script_file=__file__,version="",commitdate="",builddate=""): if os.name == 'nt': path_base = os.path.join(os.getenv('APPDATA'),"explor") else: path_base = os.path.expanduser("~/.config/explor") initSettings(path_base) args = parse_args(script_file) initYmlSettings(path_base) app = QApplication(sys.argv) app.setApplicationName("explor") app.setQuitOnLastWindowClosed(True) app_icon = QIcon(':/img/icon_explor.png') app.setWindowIcon(app_icon) installExceptionHook() ResourceManager.instance().load() version_info = (version,commitdate,builddate) window = MainWindow(version_info,args.path,args.path_r) window.showWindow() sys.exit(app.exec_())
def item2img(self,item): l = ResourceManager.LINK if item['isLink'] else 0 if item['isDir']: return ResourceManager.instance().get(l|ResourceManager.DIRECTORY) _,ext = self.view.splitext(item['name']) return ResourceManager.instance().get(l|ResourceManager.instance().getExtType(ext))
def initColumns(self): """ self.columns.append( TableColumnImage(self,'isDir',"Icon") ) self.columns[-1].setShortName("") self.columns[-1].setTextTransform( lambda item,_ : self.item2img(item) ) self.columns[-1].width = ResourceManager.instance().width() + 4 # arbitrary pad, image is centered self.columns.append( TableDualColumn(self,'name',"File Name") ) self.columns[-1].setSecondaryTextTransform(lambda r,item : format_bytes(r['size'])) """ self.columns.append(TableColumnImage(self, 'type', "Icon")) self.columns[-1].setShortName("") self.columns[-1].setTextTransform(lambda item, _: self.item2img(item)) self.columns[-1].width = ResourceManager.instance().width( ) + 4 # arbitrary pad, image is centered self.columns.append(EditTextColumn(self, 'name', "File Name")) self.columns[-1].setWidthByCharCount(35) self.columns[-1].commitText.connect(self._onCommitText) self.columns[-1].createFile.connect(self._onCreateFile) self.columns[-1].createDirectory.connect(self._onCreateDirectory) self.columns[-1].editorStart.connect(self.onEditorStart) self.columns[-1].editorFinished.connect(self.onEditorFinished) _rule1 = lambda item: "isHidden" in item or self.data.hidden(item[ 'name']) rule1 = lambda row: exception_guard(_rule1, self.data, row, "is item hidden") self.addRowTextColorComplexRule(rule1, QColor(0, 0, 200)) _rule2 = lambda item: item['isLink'] == DataSource.IS_LNK_BROKEN rule2 = lambda row: exception_guard(_rule2, self.data, row, "is item link") self.addRowTextColorComplexRule(rule2, QColor(200, 0, 0)) _rule3 = lambda item: 'mode' in item and stat.S_IXUSR & item[ 'mode'] and not item['isDir'] rule3 = lambda row: exception_guard(_rule3, self.data, row, "is item dir") self.addRowTextColorComplexRule(rule3, QColor(30, 125, 45)) self.columns.append(TableColumn(self, 'size', "Size")) self.columns[-1].setTextTransform( lambda item, _: format_bytes(item['size'])) self.columns[-1].setTextAlign(Qt.AlignRight) self.columns[-1].setWidthByCharCount(7) self.columns.append(TableColumn(self, 'mtime', "Modified Date")) self.columns[-1].setTextTransform( lambda item, _: self.getFormatedDate(item)) self.columns[-1].setShortName("Date") self.columns[-1].setDefaultSortReversed(True) self.columns[-1].setTextAlign(Qt.AlignRight) self.columns[-1].setWidthByCharCount(13) self.columns.append(TableColumn(self, 'mode', "Permissions")) self.columns[-1].setTextTransform(lambda _, v: format_mode(v)) self.columns[-1].setShortName("Mode") self.columns[-1].setTextAlign(Qt.AlignRight) self.columns[-1].setWidthByCharCount(10)
def load_resource(self, path): """return a in-memory representation of path""" ext = os.path.splitext(path)[1] kind = ResourceManager.instance().getExtType(ext) # todo read the first few bytes to check the kind source = self.parent.getSource() # this actually speeds up DirectorySource>EBFSSource>ZipSource # cases. I believe because there are more sequence points for # the threading system to preempt this thread. data = b"" with source.open(path, "rb") as rb: buf = rb.read(32 * 1024) while buf: data += buf buf = rb.read(32 * 1024) if kind == ResourceManager.GIF: return QByteArray(data) elif kind == ResourceManager.IMAGE: img = QImage.fromData(data) # note: # return (img, None, (0,0,0)) # to disable pre-scaling return (img, None, (0, 0, 0)) # prescale the image #w,h = self.parent.getScaleSize() #mode = self.parent.getScaleMode() #map = scale_image_to_map(img,w,h,mode) #params = (w,h,mode) # if img.width()*img.height() > 1920*1080: # img = scale_image(img,1920,1080) #map = QPixmap.fromImage(img) # return (img,map,params) else: return None
def validateResource(self, path): ext = self.source.splitext(path)[1] kind = ResourceManager.instance().getExtType(ext) return kind in (ResourceManager.IMAGE, ResourceManager.GIF)
def contextMenu(self, event, model, items): is_files = all(not item['isDir'] for item in items) is_dirs = all(item['isDir'] for item in items) ctxtmenu = QMenu(model) # file manipulation options menu = ctxtmenu.addMenu("New") menu.addAction("Empty File", lambda: model.action_touch_begin()) menu.addAction("Folder", lambda: model.action_mkdir_begin()) if len(items) > 0: menu = ctxtmenu.addMenu("Archive") menu.addAction("Add to 7z") menu.addAction("Add to zip") if len(items) == 1 and extract_supported(items[0]['name']): menu.addAction("Extract to *") menu.addAction("Extract to <named>") if len(items) == 1: # TODO: someday I should have a way to enable "default programs" # by file extension, and open alternatives # for now, there are only three classes I care about # todo: # Open as ... # Image (edit) -- paint # Image (view) -- irfanview # Internet -- firefox # create a tab in the settings for managing context menus # the tab should be a table with the following values # Name -- displayed in the open as context menu # executable -- path to program # possible option, each row in the table contains an ENUM value # define Text, Audio, Vido, Image etc and the first defined # of each type in the table iss considered "primary" # and used by the model # possible option : finally implementing per extension # handlers -- with some handlers that are "default" and always # show for any extention, and another "default" -- which # is just open native for files with no known extension # this information should be stored in the database so that # the command line interface can access it menu = ctxtmenu.addMenu("Open As ...") menu.addAction("Text", lambda: model.action_edit(items[0])) menu.addAction("Audio", lambda: model.action_openas_audio(items[0])) menu.addAction("Video", lambda: model.action_openas_video(items[0])) menu.addAction("Native", lambda: model.action_openas_native(items[0])) # add a menu option for opening the selected image in a # pop up window ext = model.view.splitext(items[0]['name'])[1] kind = ResourceManager.instance().getExtType(ext) if kind in (ResourceManager.IMAGE, ResourceManager.GIF): ctxtmenu.addAction("View Image",lambda : \ self.viewImage.emit(model.view, items[0]['name'])) self._ctxtMenu_addFileOperations1(ctxtmenu, model, items) ctxtmenu.addSeparator() if model.view.islocal(): if len(items) == 1: if not items[0]['isDir'] and items[0][ 'isLink'] != DataSource.IS_LNK_BROKEN: ctxtmenu.addAction("Edit", lambda: model.action_edit(items[0])) elif items[0]['isLink']: ctxtmenu.addAction( "Edit Link", lambda: model.action_edit_link(items[0])) ctxtmenu.addSeparator() if model.view.islocal() and Settings.instance()['cmd_diff_files']: ctxtmenu.addSeparator() if len(items) == 1: if self.act_diff_left_path is None: ctxtmenu.addAction( "Compare: Set Left", lambda: self.action_compare_set_left(model, items[0])) else: name = model.view.split(self.act_diff_left_path)[1] ctxtmenu.addAction( "Compare: Set Left", lambda: self.action_compare_set_left(model, items[0])) ctxtmenu.addAction( "Compare to: %s" % name, lambda: self.action_compare(model, items[0])) elif len(items) == 2: ctxtmenu.addAction("Compare 2 Items", lambda: self.action_compare_2(model, items)) self._ctxtMenu_addFileOperations2(ctxtmenu, model, items) if model.view.islocal(): ctxtmenu.addAction(QIcon(":/img/app_open.png"), "Open in Explorer", model.action_open_directory) ctxtmenu.addAction("Open in Terminal", model.action_open_term) ctxtmenu.addAction("Refresh", model.action_refresh) action = ctxtmenu.exec_(event.globalPos())
def initSettings(path_base): if not os.path.exists(path_base): os.makedirs(path_base) settings_db_path = os.path.join(path_base,"settings.db") sqlstore = SQLStore(settings_db_path) Settings.init( sqlstore ) Settings.instance()['database_directory']=path_base data = {} # basic associations by extension # TODO: pull the defaults out the resource manager, # settings menu can modify these (and update resource manager) fAssoc = ResourceManager.instance().getFileAssociation # TODO: resource manager doesnt keep track of text files, should it? data['ext_text'] = [".txt",".log",".md",] data['ext_archive'] = fAssoc(ResourceManager.ARCHIVE) data['ext_image'] = fAssoc(ResourceManager.IMAGE) data['ext_audio'] = fAssoc(ResourceManager.SONG); data['ext_movie'] = fAssoc(ResourceManager.MOVIE) data['ext_document'] = fAssoc(ResourceManager.DOCUMENT) data['ext_code'] = fAssoc(ResourceManager.CODE) if sys.platform == 'darwin': cmd_edit_text = "\"/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl\" \"%s\"" cmd_edit_image = "" cmd_open_native = "open \"%s\"" cmd_launch_terminal = "open -b com.apple.terminal \"%s\"" cmd_diff_files = "\"/Applications/Beyond Compare.app/Contents/MacOS/bcomp\" \"%s\" \"%s\"" cmd_play_audio = "/Applications/VLC.app/Contents/MacOS/VLC \"%s\"" cmd_play_video = "/Applications/VLC.app/Contents/MacOS/VLC \"%s\"" cmd_vagrant = "vagrant" elif sys.platform=="win32": cmd_edit_text = "\"C:\\Program Files\\Sublime Text 3\\subl.exe\" \"%s\"" #cmd_edit_image = "mspaint.exe \"%s\"" cmd_edit_image = "pythonw \"D:\\Dropbox\\Code\\packages\\PyPaint\\PyPaint.py\" \"%s\"" cmd_open_native = "explorer \"%s\"" cmd_launch_terminal = "start /D \"%s\"" cmd_diff_files = "" cmd_play_audio = "" cmd_play_video = "" cmd_vagrant = "" else: cmd_edit_text = "subl3 \"%s\"" cmd_edit_image = "" # nemo caja thunar dolphin cmd_open_native = "nemo \"%s\"" cmd_launch_terminal = "xterm -e \"cd %s; bash\"" cmd_diff_files = "" cmd_play_audio = "" cmd_play_video = "" cmd_vagrant = "" data["cmd_edit_text"] = cmd_edit_text data["cmd_edit_image"] = cmd_edit_image data["cmd_open_native"] = cmd_open_native data["cmd_launch_terminal"] = cmd_launch_terminal data["cmd_diff_files"] = cmd_diff_files data["cmd_play_audio"] = cmd_play_audio data["cmd_play_video"] = cmd_play_video data["cmd_vagrant"] = cmd_vagrant # view_show_hidden is the default value for new tabs # each widget supports an individual setting, which can be toggled. data['view_show_hidden'] = True Settings.instance().setMulti(data,False)
def __init__(self, version_info, defaultpath, defaultpath_r=""): """ defaultpath: if empty default to the users home """ super(MainWindow, self).__init__() self.sources = set() self.version, self.versiondate, self.builddate = version_info self.initMenuBar() self.initStatusBar() self.splitter = QSplitter(self) self.pain_main = Pane(self) self.dashboard = Dashboard(self) # controller maintains state between tabs self.source = DirectorySource() self.controller = ExplorController(self) self.controller.forceReload.connect(self.refresh) self.controller.submitJob.connect(self.dashboard.startJob) self.controller.viewImage.connect(self.onViewImage) self.btn_newTab = QToolButton(self) self.btn_newTab.clicked.connect(self.newTab) self.btn_newTab.setIcon(QIcon(":/img/app_plus.png")) self.quickview = QuickAccessTable(self) self.quickview.changeDirectory.connect(self.onChangeDirectory) self.quickview.changeDirectoryRight.connect( self.onChangeDirectoryRight) self.wfctrl = WatchFileController(self.source) self.wfctrl.watchersChanged.connect(self.onWatchersChanged) self.wfview = WatchFileTable(self.wfctrl, self) self.wfview.changeDirectory.connect(self.onChangeDirectory) self.tabview = TabWidget(self) self.tabview.tabBar().setMovable(True) self.tabview.setCornerWidget(self.btn_newTab) self.tabview.tabCloseRequested.connect(self.onTabCloseRequest) self.tabview.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.tabview.tabBar().setUsesScrollButtons(True) self.tabview.tabBar().setElideMode(Qt.ElideNone) self.pain_main.addWidget(self.tabview) self.pain_main.addWidget(self.dashboard) self.calculator = Calculator(self) self.clock = Clock(self) self.image_view = ImageView(self) self.image_view.image_extensions = ResourceManager.instance(). \ getFileAssociation(ResourceManager.IMAGE) self.wview = QWidget() self.vbox_view = QVBoxLayout(self.wview) self.vbox_view.addWidget(self.clock) self.vbox_view.setContentsMargins(0, 0, 0, 0) self.vbox_view.addWidget(self.quickview.container) self.vbox_view.addWidget(self.wfview.container) self.vbox_view.addWidget(self.image_view) self.vbox_view.addWidget(self.calculator) self.splitter.addWidget(self.wview) self.splitter.addWidget(self.pain_main) self.setCentralWidget(self.splitter) # create the first tab self.newTab(defaultpath, defaultpath_r) self.xcut_refresh = QShortcut(QKeySequence(Qt.Key_Escape), self) self.xcut_refresh.activated.connect(self.refresh) self.imgview_dialog = None