diff --git a/TermTk/TTkCore/constant.py b/TermTk/TTkCore/constant.py index a8b4a418..fd72f8bf 100644 --- a/TermTk/TTkCore/constant.py +++ b/TermTk/TTkCore/constant.py @@ -64,10 +64,23 @@ class TTkConstant: HORIZONTAL = 0x01 VERTICAL = 0x02 - # Scroll Bar Policy - ScrollBarAsNeeded = 0x00 - ScrollBarAlwaysOff = 0x01 - ScrollBarAlwaysOn = 0x02 + class ScrollBarPolicy: + ScrollBarAsNeeded = 0x00 + ScrollBarAlwaysOff = 0x01 + ScrollBarAlwaysOn = 0x02 + + ScrollBarAsNeeded = ScrollBarPolicy.ScrollBarAsNeeded + ScrollBarAlwaysOff = ScrollBarPolicy.ScrollBarAlwaysOff + ScrollBarAlwaysOn = ScrollBarPolicy.ScrollBarAlwaysOn + + class CheckState: + Unchecked = 0x00 + PartiallyChecked = 0x01 + Checked = 0x02 + + Unchecked = CheckState.Unchecked + PartiallyChecked = CheckState.PartiallyChecked + Checked = CheckState.Checked # Keys diff --git a/TermTk/TTkWidgets/checkbox.py b/TermTk/TTkWidgets/checkbox.py index e35f8fe5..f1934f39 100644 --- a/TermTk/TTkWidgets/checkbox.py +++ b/TermTk/TTkWidgets/checkbox.py @@ -29,18 +29,24 @@ from TermTk.TTkCore.color import TTkColor from TermTk.TTkWidgets.widget import * class TTkCheckbox(TTkWidget): - __slots__ = ('_checked', 'clicked') + __slots__ = ('_checked', 'clicked', 'stateChanged') def __init__(self, *args, **kwargs): TTkWidget.__init__(self, *args, **kwargs) self._name = kwargs.get('name' , 'TTkCheckbox' ) # Define Signals - # self.cehcked = pyTTkSignal() + self.stateChanged = pyTTkSignal(int) self.clicked = pyTTkSignal(bool) self._checked = kwargs.get('checked', False ) self.setMinimumSize(3, 1) self.setMaximumHeight(1) self.setFocusPolicy(TTkK.ClickFocus + TTkK.TabFocus) + def checkState(self): + if self._checked: + return TTkK.Checked + else: + return TTkK.Unchecked + def paintEvent(self): if self.hasFocus(): borderColor = TTkCfg.theme.checkboxBorderColorFocus @@ -58,5 +64,6 @@ class TTkCheckbox(TTkWidget): def mousePressEvent(self, evt): self._checked = not self._checked self.clicked.emit(self._checked) + self.stateChanged.emit(self.checkState()) self.update() return True diff --git a/TermTk/TTkWidgets/treewidget.py b/TermTk/TTkWidgets/treewidget.py index 39ff4d9f..d57e51fa 100644 --- a/TermTk/TTkWidgets/treewidget.py +++ b/TermTk/TTkWidgets/treewidget.py @@ -73,7 +73,7 @@ class _TTkDisplayedTreeItem(TTkWidget): def paintEvent(self): if self._isLeaf: - self._canvas.drawText(pos=(self._depth, 0), text="⏺") + self._canvas.drawText(pos=(self._depth, 0), text="•") self._canvas.drawText(pos=(self._depth+2, 0), text=self._text) diff --git a/demo/demo.py b/demo/demo.py index b83a968a..a22f5391 100755 --- a/demo/demo.py +++ b/demo/demo.py @@ -82,65 +82,8 @@ def main(): else: winTabbed1 = ttk.TTkWindow(parent=root,pos=(0,0), size=(120,40), title="Test Tab", border=True, layout=ttk.TTkGridLayout()) border = True - demoShowcase(winTabbed1, border) - - - win_table1 = ttk.TTkWindow(parent=root,pos = (3,3), size=(150,40), title="Test Table 1", layout=ttk.TTkHBoxLayout(), border=True) - table1 = ttk.TTkTable(parent=win_table1, selectColor=ttk.TTkColor.bg('#882200')) - - win_table2 = ttk.TTkWindow(parent=root,pos = (15,5), size=(100,30), title="Test Table 2 Default", layout=ttk.TTkHBoxLayout(), border=True) - table2 = ttk.TTkTable(parent=win_table2) - - win_table3 = ttk.TTkWindow(parent=root,pos = (15,5), size=(130,40), title="Test Table 2 Default", layout=ttk.TTkHBoxLayout(), border=True) - table3 = ttk.TTkTable(parent=win_table3, showHeader=False) - - table1.setColumnSize((5,10,-1,10,20)) - table1.setAlignment(( - ttk.TTkK.LEFT_ALIGN, - ttk.TTkK.RIGHT_ALIGN, - ttk.TTkK.LEFT_ALIGN, - ttk.TTkK.LEFT_ALIGN, - ttk.TTkK.CENTER_ALIGN - )) - table1.setHeader(("id","Name","Sentence","Word","center")) - table1.setColumnColors(( - ttk.TTkColor.fg('#888800', modifier=ttk.TTkColorGradient(increment=6)), - ttk.TTkColor.RST, - ttk.TTkColor.fg('#00dddd', modifier=ttk.TTkColorGradient(increment=-4)), - ttk.TTkColor.RST, - ttk.TTkColor.fg('#cccccc', modifier=ttk.TTkColorGradient(increment=-2)) - )) - table2.setColumnSize((5,10,-1,10,20)) - table2.setHeader(("id","Name","Sentence","Word","")) - - table3.setColumnSize((5,10,-1,10,20)) - table3.setAlignment(( - ttk.TTkK.LEFT_ALIGN, - ttk.TTkK.RIGHT_ALIGN, - ttk.TTkK.LEFT_ALIGN, - ttk.TTkK.LEFT_ALIGN, - ttk.TTkK.CENTER_ALIGN - )) - table3.setHeader(("id","Name","Sentence","Word","center")) - table3.setColumnColors(( - ttk.TTkColor.fg('#ffff00', modifier=ttk.TTkColorGradient(increment=6)), - ttk.TTkColor.fg('#ff0000', modifier=ttk.TTkColorGradient(increment=6)), - ttk.TTkColor.fg('#00ffff', modifier=ttk.TTkColorGradient(increment=-8)), - ttk.TTkColor.fg('#00ff00', modifier=ttk.TTkColorGradient(increment=-4)), - ttk.TTkColor.fg('#cccccc', modifier=ttk.TTkColorGradient(increment=-2)) - )) - - - table3.appendItem((" - ","","You see it's all clear, You were meant to be here, From the beginning","","")) - for i in range(0, 5): - table1.appendItem((str(i), getWord(), getSentence(8,30), getWord(), getWord())) - for i in range(0, 5): - table2.appendItem((str(i), getWord(), getSentence(8,30), getWord(), getWord())) - for i in range(0, 35): - table3.appendItem((str(i), getWord(), getSentence(8,30), getWord(), getWord())) - - table3.appendItem((" - ","This is the end", "Beautiful friend, This is the end My only friend", "the end", "...")) + demoShowcase(winTabbed1, border) root.mainloop() diff --git a/demo/tlogg.py b/demo/tlogg.py index 4062b728..220f0851 100755 --- a/demo/tlogg.py +++ b/demo/tlogg.py @@ -42,14 +42,43 @@ from TermTk.TTkCore.signal import pyTTkSlot, pyTTkSignal from TermTk.TTkAbstract.abstractscrollarea import TTkAbstractScrollArea from TermTk.TTkAbstract.abstractscrollview import TTkAbstractScrollView + + +''' + w1 w3 w2 w5 + Buffer |----|----|----|----| + | \ / \ + | x \ + | / \ \ + File |----|----|----|----|----|----| + w1 w2 w3 w4 w5 w6 +''' class _FileBuffer(): - __slots__ = ('_filename', '_indexes', '_fd', '_lastline') - def __init__(self, filename, window): + class _Page: + __slots__ = ('_page', '_size', '_buffer') + def __init__(self, page, size): + self._page = page + self._size = size + self._buffer = [""]*self._size + #TTkLog.debug(f"{self._buffer}") + @property + def buffer(self): + return self._buffer + @property + def page(self): + return self._page + + __slots__ = ('_filename', '_indexes', '_fd', '_lastline', '_pages', '_buffer', '_window', '_numW', '_width') + def __init__(self, filename, window, numWindows): + self._window = window + self._numW = numWindows self._filename = filename self._indexes = [] + self._width=0 self.createIndex() + self._buffer = [None]*self._numW + self._pages = [None]*(1+self.getLen()//window) self._fd = open(self._filename,'r') - self._lastline = {'line':0, 'txt':self._fd.readline()} def __del__(self): self._fd.close() @@ -57,13 +86,38 @@ class _FileBuffer(): def getLen(self): return len(self._indexes) + def getWidth(self, indexes=None): + return self._width + + def getLineDirect(self, line): + if line >= self.getLen(): + return "" + self._fd.seek(self._indexes[line]) + return self._fd.readline() + def getLine(self, line): if line >= self.getLen(): return "" - if self._lastline['line'] != line : + page = line//self._window + offset = line%self._window + if self._pages[page] == None: + # Dispose of the pages to the bottom + dispose = self._buffer.pop(0) + if dispose is not None: + self._pages[dispose.page] = None + self._pages[page] = self._Page(page, self._window) + self._buffer.append(self._pages[page]) self._fd.seek(self._indexes[line]) - self._lastline = {'line':line, 'txt':self._fd.readline()} - return self._lastline['txt'] + buffer = self._pages[page].buffer + for i in range(self._window): + buffer[i] = self._fd.readline() + self._width = max(self._width,len(buffer[i])) + else: + # Push the page to the top of the buffer + i = self._buffer.index(self._pages[page]) + p = self._buffer.pop(i) + self._buffer.append(p) + return self._pages[page].buffer[offset] def createIndex(self): @@ -76,7 +130,7 @@ class _FileBuffer(): self._indexes.append(offset) offset += len(line) - def search(self, regex): + def searchRe(self, regex): indexes = [] id = 0 rr = re.compile(regex) @@ -88,6 +142,16 @@ class _FileBuffer(): id += 1 return indexes + def search(self, txt): + indexes = [] + id = 0 + with open(self._filename,'r') as infile: + for line in infile: + if txt in line: + indexes.append(id) + id += 1 + return indexes + class _FileViewer(TTkAbstractScrollView): __slots__ = ('_fileBuffer', '_indexes', '_indexesMark', '_indexexSearched', '_selected') def __init__(self, *args, **kwargs): @@ -118,10 +182,10 @@ class _FileViewer(TTkAbstractScrollView): def viewFullAreaSize(self) -> (int, int): if self._indexes is None: - w = 300 + w = 2+self._fileBuffer.getWidth() h = self._fileBuffer.getLen() else: - w = 300 + w = 2+self._fileBuffer.getWidth(self._indexes) h = len(self._indexes) return w , h @@ -141,18 +205,18 @@ class _FileViewer(TTkAbstractScrollView): color = TTkColor.fg("#ff0000") else: color = TTkColor.fg("#0000ff") - self.getCanvas().drawText(pos=(0,i), text="⏺", color=color) - self.getCanvas().drawText(pos=(2,i), text=self._fileBuffer.getLine(i+oy).replace('\t',' ').replace('\n','') ) + self.getCanvas().drawText(pos=(0,i), text="●", color=color) + self.getCanvas().drawText(pos=(2,i), text=self._fileBuffer.getLine(i+oy).replace('\t',' ').replace('\n','')[ox:] ) else: for i in range(min(self.height(),len(self._indexes))): if self._indexes[i+oy] in self._indexesSearched: color = TTkColor.fg("#ff0000") else: color = TTkColor.fg("#0000ff") - self.getCanvas().drawText(pos=(0,i), text="⏺", color=color) + self.getCanvas().drawText(pos=(0,i), text="●", color=color) self.getCanvas().drawText( pos=(2,i), - text=self._fileBuffer.getLine(self._indexes[i+oy]).replace('\t',' ').replace('\n','') ) + text=self._fileBuffer.getLineDirect(self._indexes[i+oy]).replace('\t',' ').replace('\n','')[ox:] ) @@ -190,18 +254,22 @@ def main(): # Define the bottom layout widgets bottomLayoutSearch = TTkHBoxLayout() - bls_label = TTkLabel(text="Text:", maxWidth=6) + bls_label_1 = TTkLabel(text="Text:", maxWidth=6) bls_textbox = TTkLineEdit() - bls_search = TTkButton(text="Search", maxWidth=7) + bls_label_2 = TTkLabel(text="re:", maxWidth=3) + bls_cb_re = TTkCheckbox(maxWidth=3) + bls_search = TTkButton(text="Search", maxWidth=10) - bottomLayoutSearch.addWidget(bls_label) + bottomLayoutSearch.addWidget(bls_label_2) + bottomLayoutSearch.addWidget(bls_cb_re) + bottomLayoutSearch.addWidget(bls_label_1) bottomLayoutSearch.addWidget(bls_textbox) bottomLayoutSearch.addWidget(bls_search) bottomFrame.layout().addItem(bottomLayoutSearch) # Define the main file Viewer - fileBuffer = _FileBuffer(file, 5000) + fileBuffer = _FileBuffer(file, 0x1000, 0x10) topViewer = FileViewer(parent=topFrame, filebuffer=fileBuffer) # Define the Search Viewer bottomViewer = FileViewer(parent=bottomFrame, filebuffer=fileBuffer) @@ -210,18 +278,22 @@ def main(): bottomViewport.showIndexes([]) class _search: - def __init__(self,tb,fb,tvp,bvp): + def __init__(self,tb,fb,cb,tvp,bvp): self.tb=tb self.fb=fb + self.cb=cb self.tvp=tvp self.bvp=bvp def search(self): searchtext = self.tb.text() - indexes = self.fb.search(searchtext) + if self.cb.checkState() == TTkK.Checked: + indexes = self.fb.searchRe(searchtext) + else: + indexes = self.fb.search(searchtext) self.bvp.showIndexes(indexes) self.bvp.searchedIndexes(indexes) self.tvp.searchedIndexes(indexes) - _s = _search(bls_textbox,fileBuffer,topViewer.viewport(),bottomViewport) + _s = _search(bls_textbox,fileBuffer,bls_cb_re,topViewer.viewport(),bottomViewport) bls_search.clicked.connect(_s.search) bls_textbox.returnPressed.connect(_s.search) diff --git a/tests/utf-8/first.50000.txt b/tests/utf-8/first.50000.txt new file mode 100644 index 00000000..b936c431 Binary files /dev/null and b/tests/utf-8/first.50000.txt differ