diff --git a/README.md b/README.md index b23b6e48..83fd785e 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ cprofilev -f profiler.txt ## Related Projects - Python + - [urwid](https://github.com/urwid/urwid) - Console user interface library for Python - [pyTermGUI](https://github.com/bczsalba/pytermgui) - A simple yet powerful TUI framework for your Python (3.7+) applications - [Textual](https://github.com/Textualize/textual) - TUI (Text User Interface) framework for Python inspired by modern web development - [Rich](https://github.com/Textualize/rich) - Python library for rich text and beautiful formatting in the terminal diff --git a/TermTk/TTkCore/constant.py b/TermTk/TTkCore/constant.py index 8062842a..12f15502 100644 --- a/TermTk/TTkCore/constant.py +++ b/TermTk/TTkCore/constant.py @@ -187,6 +187,16 @@ class TTkConstant: CENTER_ALIGN = Alignment.CENTER_ALIGN JUSTIFY = Alignment.JUSTIFY + class FileMode(): + AnyFile = 0 #The name of a file, whether it exists or not. + # ExistingFile = 1 #The name of a single existing file. + Directory = 2 #The name of a directory. Both files and directories are displayed. However, the native Windows file dialog does not support displaying files in the directory chooser. + # ExistingFiles = 3 #The names of zero or more existing files. + + # AnyFile = FileMode.AnyFile + # ExistingFile = FileMode.ExistingFile + # Directory = FileMode.Directory + # ExistingFiles = FileMode.ExistingFiles # LayoutItem Types class LayoutItemTypes: diff --git a/TermTk/TTkWidgets/TTkPickers/filepicker.py b/TermTk/TTkWidgets/TTkPickers/filepicker.py index c71c5197..efcf4b94 100644 --- a/TermTk/TTkWidgets/TTkPickers/filepicker.py +++ b/TermTk/TTkWidgets/TTkPickers/filepicker.py @@ -24,7 +24,6 @@ import os import re -import datetime from TermTk.TTkCore.color import TTkColor @@ -60,16 +59,19 @@ from TermTk.TTkWidgets.TTkModelView.filetreewidgetitem import TTkFileTreeWidgetI ''' class TTkFileDialogPicker(TTkWindow): - __slots__ = ('_path', '_recentPath', '_recentPathId', '_filters', '_filter', '_caption', + __slots__ = ('_path', '_recentPath', '_recentPathId', '_filters', '_filter', '_caption', '_fileMode', # Widgets '_fileTree', '_lookPath', '_btnPrev', '_btnNext', '_btnUp', '_fileName', '_fileType', '_btnOpen', '_btnCancel', # Signals - 'filePicked') + 'pathPicked', 'filePicked', 'filesPicked', 'folderPicked') def __init__(self, *args, **kwargs): # Signals + self.pathPicked = pyTTkSignal(str) self.filePicked = pyTTkSignal(str) + self.filesPicked = pyTTkSignal(list) + self.folderPicked = pyTTkSignal(str) TTkWindow.__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkFileDialogPicker' ) @@ -77,10 +79,11 @@ class TTkFileDialogPicker(TTkWindow): self._recentPathId = -1 self._recentPath = [] - self._path = kwargs.get('path','.') - self._filter = '*' - self._filters = kwargs.get('filter','All Files (*)') - self._caption = kwargs.get('caption','File Dialog') + self._path = kwargs.get('path','.') + self._filter = '*' + self._filters = kwargs.get('filter','All Files (*)') + self._caption = kwargs.get('caption','File Dialog') + self._fileMode = kwargs.get('fileMode',TTkK.FileMode.AnyFile) self.setTitle(self._caption) self.setLayout(TTkGridLayout()) @@ -162,7 +165,14 @@ class TTkFileDialogPicker(TTkWindow): @pyTTkSlot(str) def _checkFileName(self, fileName): - if os.path.exists(fileName) and os.path.isfile(fileName): + valid = False + if self._fileMode == TTkK.FileMode.AnyFile: + valid = os.path.exists(fileName) and os.path.isfile(fileName) + elif self._fileMode == TTkK.FileMode.Directory: + valid = os.path.exists(fileName) and os.path.isdir(fileName) + else: + pass + if valid: self._btnOpen.setEnabled() else: self._btnOpen.setDisabled() @@ -171,12 +181,19 @@ class TTkFileDialogPicker(TTkWindow): def _open(self): fileName = self._fileName.text() if not os.path.exists(fileName): return - self.filePicked.emit(fileName) + if self._fileMode == TTkK.FileMode.AnyFile and not os.path.isfile(fileName): return + if self._fileMode == TTkK.FileMode.Directory and not os.path.isdir(fileName): return + if self._fileMode == TTkK.FileMode.AnyFile: + self.filePicked.emit(fileName) + if self._fileMode == TTkK.FileMode.Directory: + self.folderPicked.emit(fileName) + self.pathPicked.emit(fileName) self.close() @pyTTkSlot(TTkFileTreeWidgetItem, int) def _selectedItem(self, item, _): - if item.getType() != item.FILE: return + if self._fileMode == TTkK.FileMode.AnyFile and item.getType() != item.FILE: return + if self._fileMode == TTkK.FileMode.Directory and item.getType() != item.DIR : return self._fileName.setText(item.path()) @pyTTkSlot(TTkFileTreeWidgetItem, int) @@ -187,6 +204,12 @@ class TTkFileDialogPicker(TTkWindow): elif os.path.isfile(path): self._open() + def filemode(self): + return self._fileMode + + def setFileMode(self, fileMode): + self._fileMode = fileMode + def _openPrev(self): if self._recentPathId<=0 or self._recentPathId>=len(self._recentPath): self._btnPrev.setDisabled() @@ -239,84 +262,6 @@ class TTkFileDialogPicker(TTkWindow): if not path or path=='/': break return ret - - @staticmethod - def _getFileItems(path): - path = os.path.abspath(path) - if not os.path.exists(path): return [] - dir_list = os.listdir(path) - ret = [] - for n in dir_list: - nodePath = os.path.join(path,n) - - def _getStat(_path): - info = os.stat(_path) - time = datetime.datetime.fromtimestamp(info.st_ctime).strftime('%Y-%m-%d %H:%M:%S') - if info.st_size > (1024*1024*1024): - size = f"{info.st_size/(1024*1024*1024):.2f} GB" - if info.st_size > (1024*1024): - size = f"{info.st_size/(1024*1024):.2f} MB" - elif info.st_size > 1024: - size = f"{info.st_size/1024:.2f} KB" - else: - size = f"{info.st_size} bytes" - return time, size, info.st_ctime, info.st_size - - if os.path.isdir(nodePath): - if os.path.exists(nodePath): - time, _, rawTime, _ = _getStat(nodePath) - color = TTkCfg.theme.folderNameColor - else: - time, _, rawTime, _ = "" - color = TTkCfg.theme.failNameColor - - if os.path.islink(nodePath): - name = TTkString()+TTkCfg.theme.linkNameColor+n+'/'+TTkColor.RST+' -> '+TTkCfg.theme.folderNameColor+os.readlink(nodePath) - typef = "Folder Link" - else: - name = TTkString()+color+n+'/' - typef = "Folder" - - ret.append(TTkFileTreeWidgetItem( - [ name, "", typef, time], - raw = [ n , -1 , typef , rawTime ], - path=nodePath, - type=TTkFileTreeWidgetItem.DIR, - icon=TTkString() + TTkCfg.theme.folderIconColor + TTkCfg.theme.fileIcon.folderClose + TTkColor.RST, - childIndicatorPolicy=TTkK.ShowIndicator)) - - elif os.path.isfile(nodePath) or os.path.islink(nodePath): - if os.path.exists(nodePath): - time, size, rawTime, rawSize = _getStat(nodePath) - if os.access(nodePath, os.X_OK): - color = TTkCfg.theme.executableColor - typef="Exec" - else: - color = TTkCfg.theme.fileNameColor - typef="File" - else: - time, size, rawTime, rawSize = "", "", 0, 0 - color = TTkCfg.theme.failNameColor - typef="Broken" - - if os.path.islink(nodePath): - name = TTkString()+TTkCfg.theme.linkNameColor+n+TTkColor.RST+' -> '+color+os.readlink(nodePath) - typef += " Link" - else: - name = TTkString()+color+n - - _, ext = os.path.splitext(n) - if ext: ext = f"{ext[1:]} " - ret.append(TTkFileTreeWidgetItem( - [ name, size, typef, time], - raw = [ n , rawSize , typef , rawTime ], - path=nodePath, - type=TTkFileTreeWidgetItem.FILE, - icon=TTkString() + TTkCfg.theme.fileIconColor + TTkCfg.theme.fileIcon.getIcon(n) + TTkColor.RST, - childIndicatorPolicy=TTkK.DontShowIndicator)) - return ret - - class TTkFileDialog: def getOpenFileName(caption, dir=".", filter="All Files (*)", options=None): pass \ No newline at end of file diff --git a/demo/showcase/filepicker.py b/demo/showcase/filepicker.py index 03c5322d..f840d1e0 100755 --- a/demo/showcase/filepicker.py +++ b/demo/showcase/filepicker.py @@ -32,15 +32,20 @@ def demoFilePicker(root=None): frame = ttk.TTkFrame(parent=root, border=False) # winFP = ttk.TTkWindow(parent=frame,pos = (0,0), size=(20,10), title="Test File Pickers", border=True) - btn = ttk.TTkButton(parent=frame, pos=( 0,0), size=(8,3), border=True, text='File' ) - label = ttk.TTkLabel(parent=frame, pos=( 10,1), size=(30,1), text="...") + btn1 = ttk.TTkButton( parent=frame, pos=(0,0), size=(8,3), border=True, text='File' ) + btn3 = ttk.TTkButton( parent=frame, pos=(8,0), size=(13,3), border=True, text='Directory' ) + btn2 = ttk.TTkButton( parent=frame, pos=(21,0), size=(17,3), border=True, text='Existing File', enabled=False ) + btn4 = ttk.TTkButton( parent=frame, pos=(38,0), size=(18,3), border=True, text='Existing Files', enabled=False ) + label = ttk.TTkLabel(parent=frame, pos=(1,5), size=(30,1), text="...") - def _showDialog(): - filePicker = ttk.TTkFileDialogPicker(pos = (3,3), size=(75,24), caption="Pick a File", path=".", filter="All Files (*);;Python Files (*.py);;Bash scripts (*.sh);;Markdown Files (*.md)") - filePicker.filePicked.connect(label.setText) + + def _showDialog(fm): + filePicker = ttk.TTkFileDialogPicker(pos = (3,3), size=(75,24), caption="Pick Something", path=".", fileMode=fm ,filter="All Files (*);;Python Files (*.py);;Bash scripts (*.sh);;Markdown Files (*.md)") + filePicker.pathPicked.connect(label.setText) ttk.TTkHelper.overlay(frame, filePicker, 2, 1) - btn.clicked.connect(_showDialog) + btn1.clicked.connect(lambda : _showDialog(ttk.TTkK.FileMode.AnyFile)) + btn3.clicked.connect(lambda : _showDialog(ttk.TTkK.FileMode.Directory)) return frame @@ -58,7 +63,7 @@ def main(): root.setLayout(ttk.TTkGridLayout()) winColor1 = root else: - winColor1 = ttk.TTkWindow(parent=root,pos = (0,0), size=(50,20), title="Test File Picker", border=True, layout=ttk.TTkGridLayout()) + winColor1 = ttk.TTkWindow(parent=root,pos = (0,0), size=(58,20), title="Test File/Folder Picker", border=True, layout=ttk.TTkGridLayout()) demoFilePicker(winColor1)