def scrollButtonMove(self, event): """ :param generalgui.app.App self: :param event: """ if self.scrollWheelTarget: if not self.scrollWheelTarget.isShown(error=False): self.scrollButtonRelease() return coords = Vec2(event.x_root, event.y_root) mouseDiff = coords - self.startCoords canvasSize = Vec2(self.scrollWheelTarget.widget.winfo_width(), self.scrollWheelTarget.widget.winfo_height()) fractionMouseDiff = mouseDiff / canvasSize visibleFraction = self.getVisibleFraction(self.scrollWheelTarget) newFraction = self.startFraction - fractionMouseDiff * visibleFraction if visibleFraction.x < 1: self.scrollWheelTarget.widget.xview_moveto(newFraction.x) self.openMenuOnRelease = False if visibleFraction.y < 1: self.scrollWheelTarget.widget.yview_moveto(newFraction.y) self.openMenuOnRelease = False
def _traverse(self, checkPosFunc, startPos, step=None, maxPos=None, confine=False, maxSteps=100): if step is None: step = Vec2(0) if maxPos is None: maxPos = self.getGridSize() - 1 if maxPos == -1: return None pos = startPos.sanitize(ints=True) step.sanitize(ints=True) pos = self.confinePos(pos, maxPos) for i in range(maxSteps + 1): if (result := checkPosFunc(pos)) is not None: return result if step == 0: break pos += step if confine: pos = self.confinePos(pos, maxPos) if pos == startPos or not pos.inrange(Vec2(0, 0), maxPos): break
def test_pos(self): app = App() self.assertEqual(Vec2(), app.getWindowPos()) self.assertEqual(Vec2(1), app.getSize()) app.show(mainloop=False) self.assertEqual(True, app.getWindowPos().inrange(1, 500)) self.assertEqual(Vec2(), app.getTopLeftPos()) self.assertEqual(Vec2(200), app.getBottomRightPos()) self.assertEqual(Vec2(200), app.getSize()) app.setSize(Vec2(250, 300)) app.widget.update() self.assertEqual(Vec2(250, 300), app.getSize()) app.setSize(145) app.widget.update() self.assertEqual(Vec2(145), app.getSize()) page = Page(app, width=300, height=250) page.show(mainloop=False) self.assertEqual(Vec2(300, 250), app.getBottomRightPos()) self.assertEqual(Vec2(300, 250), app.getSize()) self.assertEqual(True, app.getMouse().inrange(-100000, 100000)) self.assertEqual(page.frame, app.getElementByPos(10)) page.remove() self.assertEqual(app, app.getElementByPos(10)) self.assertEqual(None, app.getElementByPos(-10)) self.assertEqual(None, app.getElementByPos(400))
def test_place(self): app = App() Page(app, width=200, height=200).show(mainloop=False) page = Page(app, width=10, height=10, bg="green") app.showChildren(mainloop=False) page.place(Vec2(100, 100)) self.assertEqual(Vec2(100, 100), page.getTopLeftPos())
def test_place(self): app = App() label = Label(Page(app, width=500, height=500), "testing", pack=False) label.place(Vec2(100, 100)) label.show(mainloop=False) self.assertEqual(Vec2(100, 100), label.getTopLeftPos())
def test_getGridElement(self): grid = Grid(App()) self.assertEqual(None, grid.getGridElement(Vec2(1, 0))) label = Label(grid, "hello", column=1, row=0) self.assertEqual(label, grid.getGridElement(Vec2(1, 0))) label = Label(grid, column=0, row=0) self.assertEqual(label, grid.getGridElement(Vec2(0, 0)))
def test_append(self): grid = Grid(App()) # Column 2 self.assertEqual(Vec2(2, 0), grid.appendToColumn(Label(grid, pack=False), 2)) self.assertEqual(Vec2(2, 1), grid.appendToColumn(Label(grid, pack=False), 2)) # Column 1 self.assertEqual(Vec2(1, 0), grid.appendToColumn(Label(grid, pack=False), 1)) # Row 0 self.assertEqual(Vec2(0, 0), grid.appendToRow(Label(grid, pack=False), 0)) self.assertEqual(Vec2(3, 0), grid.appendToRow(Label(grid, pack=False), 0)) self.assertEqual(Vec2(4, 0), grid.appendToRow(Label(grid, pack=False), 0)) # Row 1 self.assertEqual(Vec2(0, 1), grid.appendToRow(Label(grid, pack=False), 1)) self.assertEqual(Vec2(1, 1), grid.appendToRow(Label(grid, pack=False), 1)) # Column 0 self.assertEqual(Vec2(0, 2), grid.appendToColumn(Label(grid, pack=False), 0))
def loadDataFrame(self, df=None): """ Update cells to represent a dataFrame with any types of values. """ self.dataFrameIsLoading = True if df is not None: if not typeChecker(df, pd.DataFrame, error=False): df = pd.DataFrame(df) self.dataFrame = df df = self.dataFrame # print(df.to_string()) if self.columnKeys: size = Vec2(len(df.columns), 1) self.headerGrid.fillGrid(Frame, Vec2(1, 0), size, height=1) self.mainGrid.fillGrid(Frame, Vec2(1, 0), size, height=1) self.headerGrid.fillGrid( Label, Vec2(1, 1), size, values=df.columns, removeExcess=True, onClick=lambda e: self.sortColumn(cellValue=e), anchor="c") if self.rowKeys: size = Vec2(1, len(df.index)) self.indexGrid.fillGrid(Frame, Vec2(0, 1), size, width=1) self.mainGrid.fillGrid(Frame, Vec2(0, 1), size, width=1) self.indexGrid.fillGrid( Label, Vec2(1, 1), size, values=df.index, removeExcess=True, onClick=lambda e: self.sortRow(cellValue=e)) values = [] for row in df.itertuples(index=False): values.extend(row) self.mainGrid.fillGrid(Label, Vec2(1, 1), Vec2(df.shape[1], df.shape[0]), values=values, removeExcess=True, color=True, **self.cellConfig) self.dataFrameIsLoading = False self.syncSizes()
def scrollButton(self, event): """ :param generalgui.app.App self: :param event: """ self._checkEventForScrollTarget(event) if self.scrollWheelTarget: self.startCoords = Vec2(event.x_root, event.y_root) self.startFraction = Vec2(self.scrollWheelTarget.widget.xview()[0], self.scrollWheelTarget.widget.yview()[0]) self.scrollStyle.enable()
def appendToColumn(self, part, column): """ Append a part to column, after the last possibly existing cell :param generalgui.element.Element or generalgui.page.Page part: :param int column: Which column to append to :return: Position of filled cell """ firstEmptyPos = self.getFirstEmptyPos(Vec2(column, 0), Vec2(0, 1)) if firstEmptyPos is None: firstEmptyPos = Vec2(column, self.getGridSize().y) part.grid(firstEmptyPos) return firstEmptyPos
def test_pos(self): app = App() label = Button(Page(app), "testing", pack=False) self.assertEqual(Vec2(), label.getTopLeftPos()) self.assertEqual(Vec2(1), label.getSize()) label.show(mainloop=False) self.assertEqual(True, label.getWindowPos().inrange(1, 500)) self.assertEqual(True, label.getSize().inrange(10, 100)) self.assertEqual(True, label.getTopLeftPos().inrange(0, 100)) self.assertLess(label.getTopLeftPos(), label.getBottomRightPos()) self.assertEqual(True, label.getSize().inrange(10, 100))
def appendToRow(self, part, row): """ Append a part to column, after the last possibly existing cell :param generalgui.element.Element or generalgui.page.Page part: :param int row: Which row to append to :return: Position of filled cell """ firstEmptyPos = self.getFirstEmptyPos(Vec2(0, row), Vec2(1, 0)) if firstEmptyPos is None: firstEmptyPos = Vec2(self.getGridSize().x, row) part.grid(firstEmptyPos) return firstEmptyPos
def getGridElement(self, pos): """ Returns the element in a certain position in grid, or None :param Vec2 pos: Grid position to check """ pos = Vec2(pos) if not pos >= Vec2(0, 0): return None pos = pos.sanitize(ints=True) if slave := self.getBaseWidget().grid_slaves(column=pos.x, row=pos.y): return slave[0].element
def test_getGridSize(self): grid = Grid(App()) self.assertEqual(Vec2(0, 0), grid.getGridSize()) label1 = Label(grid, "hello", column=1, row=0) self.assertEqual(Vec2(2, 1), grid.getGridSize()) label2 = Label(grid, "hello", column=2, row=1) self.assertEqual(Vec2(3, 2), grid.getGridSize()) label2.remove() self.assertEqual(Vec2(2, 1), grid.getGridSize()) label1.remove() self.assertEqual(Vec2(0, 0), grid.getGridSize())
def getGridSize(self): """ Get current grid size as a Vec2. Only looks at cell with greatest position, so you could say (gridSize - Vec2(1)) is just pos of bottom right cell. """ size = self.getBaseWidget().grid_size() return Vec2(size[0], size[1])
def getTopLeftPos(self): """ Get top left corner of this part's widget. :param generalgui.element.Element or generalgui.page.Page or generalgui.app.App self: Element, Page or App """ return Vec2(self.getTopWidget().winfo_rootx(), self.getTopWidget().winfo_rooty()) - self.getWindowPos()
def getMouse(self): """ Get mouse vector2 from event, can be any part in whole app. :param generalgui.element.Element or generalgui.page.Page or generalgui.app.App self: Element, Page or App """ return Vec2(self.app.widget.winfo_pointerx(), self.app.widget.winfo_pointery()) - self.getWindowPos()
def getSize(self): """ Get size of this part's widget. :param generalgui.element.Element or generalgui.page.Page or generalgui.app.App self: Element, Page or App """ return Vec2(self.getTopWidget().winfo_width(), self.getTopWidget().winfo_height())
def getVisibleFraction(self, canvas): """ Get visible fraction of a canvas, not meant to be used other than by scroll feature. :param generalgui.app.App self: :param generalgui.Canvas canvas: :raises AttributeError: If canvas is not shown """ if not canvas.isShown(): raise AttributeError("Canvas is not shown") canvasSize = Vec2(canvas.widget.winfo_width(), canvas.widget.winfo_height()) scrollRegions = canvas.getWidgetConfig("scrollregion").split(" ") scrollSize = Vec2(int(scrollRegions[2]), int(scrollRegions[3])) visibleFraction = canvasSize / scrollSize return visibleFraction
def getWindowPos(self): """ Get current window position of the upper left corner. :param generalgui.element.Element or generalgui.page.Page or generalgui.app.App self: Element, Page or App """ return Vec2(self.app.widget.winfo_rootx(), self.app.widget.winfo_rooty())
def setSize(self, size): """ Set size of this part's widget. :param generalgui.element.Element or generalgui.page.Page or generalgui.app.App self: Element, Page or App :param Vec2 or Float size: """ size = Vec2(size) return self.getTopElement().widgetConfig(width=size.x, height=size.y)
def test_getVisibleFraction(self): app = App() page = Page(app, width=100, height=100, scrollable=True) page2 = Page(page, width=196, height=196) with self.assertRaises(AttributeError): app.getVisibleFraction(page.canvas) page2.show(mainloop=False) self.assertEqual(Vec2(0.5), app.getVisibleFraction(page.canvas))
def confinePos(self, pos, maxPos=None): """ Returns a confined pos between 0 and gridSize - 1 :param Vec2 pos: :param Vec2 maxPos: """ if maxPos is None: maxPos = self.getGridSize() - 1 return pos.confineTo(Vec2(0, 0), maxPos, margin=0.5)
def test_getGridPos(self): grid = Grid(App()) label = Label(Grid(App()), "hello") self.assertRaises(AttributeError, grid.getGridPos, label) label = Label(grid, "hello", column=1, row=1, pack=False) self.assertRaises(AttributeError, grid.getGridPos, label) label.pack() self.assertEqual(Vec2(1, 1), grid.getGridPos(label))
def _syncColumnKeysWidth(self, test=False): """ Sync the widths of all cells with headers """ if not self.columnKeys: return columnSize = self.headerGrid.getGridSize() mainSize = self.mainGrid.getGridSize() if columnSize.x != mainSize.x: print(self.headerGrid.getChildren()) raise AttributeError(f"Columns mismatch {columnSize}, {mainSize}") columnFrames = [] mainFrames = [] for pos in Vec2(1, 0).range(Vec2(columnSize.x - 1, 1)): columnFrame = self.headerGrid.getGridElement(pos) # print(columnFrame) columnFrame.widgetConfig(width=0) columnFrames.append(columnFrame) mainFrame = self.mainGrid.getGridElement(pos) # print(mainFrame) mainFrame.widgetConfig(width=0) mainFrames.append(mainFrame) if test: return self.app.widget.update_idletasks() for i, columnFrame in enumerate(columnFrames): mainFrame = mainFrames[i] columnWidth = columnFrame.widget.winfo_width() mainWidth = mainFrame.widget.winfo_width() if columnWidth > mainWidth: mainFrame.widgetConfig(width=columnWidth) else: columnFrame.widgetConfig(width=mainWidth)
def getFirstPatternPos(self, startPos=Vec2(0), firstStep=Vec2(0, 1), secondStep=Vec2(1, 0), maxFirstSteps=5): """ Get position of first empty pos in pattern. When firstStep has been made maxFirstSteps times we subtract all firstSteps and then make one secondStep. If maxfirststeps is 1 then only second step is used. :param Vec2 startPos: Inclusive position to start search :param Vec2 firstStep: Directional Vec2 to be used as step for each maxFirstSteps :param Vec2 secondStep: Directional Vec2 to be used as step for each time firstStep has been made maxFirstSteps times :param maxFirstSteps: Number of firstSteps before one secondStep """ pos = startPos while True: for i in range(maxFirstSteps): if not self.getGridElement(pos): return pos pos += firstStep pos = pos - firstStep * maxFirstSteps + secondStep
def _syncRowKeysHeight(self, test=False): """ Sync the heights of all cells with indexes """ if not self.rowKeys: return rowSize = self.indexGrid.getGridSize() mainSize = self.mainGrid.getGridSize() if rowSize.y != mainSize.y: raise AttributeError(f"Row mismatch {rowSize}, {mainSize}") rowFrames = [] mainFrames = [] for pos in Vec2(0, 1).range(Vec2(1, rowSize.y - 1)): rowFrame = self.indexGrid.getGridElement(pos) # print(rowFrame) rowFrame.widgetConfig(height=0) rowFrames.append(rowFrame) mainFrame = self.mainGrid.getGridElement(pos) # print(mainFrame) mainFrame.widgetConfig(height=0) mainFrames.append(mainFrame) if test: return self.app.widget.update_idletasks() for i, rowFrame in enumerate(rowFrames): mainFrame = mainFrames[i] rowHeight = rowFrame.widget.winfo_height() mainHeight = mainFrame.widget.winfo_height() if rowHeight > mainHeight: mainFrame.widgetConfig(height=rowHeight) else: rowFrame.widgetConfig(height=mainHeight)
def removeInput(self, key): """ Remove label and input pair that represents a key value. :param str key: :returns: Whether input existed to be removed or not """ if element := self.getInputElement(key): label = self.getGridElement(self.getGridPos(element) - Vec2(1, 0)) element.remove() label.remove() del self._inputElements[key] return True
def setInputElement(self, key, value): """ Set the default value of a new or existing input element, removes old if it exists. Automatically choses between Checkbutton and Entry :param str key: :param any value: """ if key in self._inputElements: self.removeInput(key) label = self.app.Label(self, key) pos = self.getFirstPatternPos(secondStep=Vec2(2, 0), maxFirstSteps=self.maxFirstSteps) label.grid(pos) if value is True or value is False: element = self.app.Checkbutton(parentPage=self, default=value) else: element = self.app.Entry(parentPage=self, default=str(value)) element.grid(pos + Vec2(1, 0)) self._inputElements[key] = element
def _checkEventForScrollTarget(self, event): if not event: return eventElement = event.widget.element if typeChecker(eventElement, "App", error=False): return pages = eventElement.getParents() for page in pages: if page.scrollable and page.mouseScroll: visibleFraction = self.getVisibleFraction(page.canvas) if not visibleFraction >= Vec2(1): self.scrollWheelTarget = page.canvas break