Bottom Page

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
 [Tkinter] Call a function when switching layouts
I am writing a flashcard program in Python 3.7 using tkinter. I have many interfaces, like one for selecting a file, and one for reviewing cards, one with a list of decks to choose from. I also have an interface to edit a card which shows the data for one card at a time, with buttons to switch to the next or previous card. I have another interface with 25 rows all showing data for one card. I have classes for keeping track of all the data being edited. I have a View class which has methods that draw the interfaces. Separate methods draw the single-edit interface and the multi-edit interface. Both methods have local functions which are triggered by the buttons in the interface, and both have a local variable which is an instance of a class to keep track of the data.

I want to be able to switch between the multiple/single editing modes and keep the data that was edited in the other interface. The method for switching between the two layouts is:
    def switchLayout(self,newLayout):
        if self.currentLayout is not None:
        self.prevLayout = self.currentLayout
        self.currentLayout = self.lay[newLayout]
I could try using a class member instead of a local variable in the two methods (the method that draws the single-edit interface, and the one that draws the multiple-edit interface). However, I don't like the idea of a class member used by only two methods. Even if I did it that way, I would need to load the data into the next widgets of the new interface when I switch the interfaces.

The class that keeps track of data keeps track of what was entered on a form if I went to another page or something. The actual widgets are stored in local variables of the method.

If there is a way the calls to pack and pack_forget on a widget could trigger an event, I think I can solve this. Can an event be triggered that way? I think my problem is that I need access to local variables of a method. If I made them global, it would clutter things. Should my class that keeps track of data also keep track of the widget objects themselves? What if it kept a reference to a function local to a method of the View class? Would that be bad practice?

The only way I can think of is to have a class member set to True when switching between single/multiple edit mode. Then I could have an event bound to the main frame widget to trigger when the mouse moves, that would call a function that check to see if the flag were true, and if it were true, update the forms, and set the flag to False. Some problems with this are that the function would be called repeatedly while the mouse is moving; to allow a user to edit only from the keyboard instead of the mouse I would need to bind more events, possibly to all widgets in the interface; and the forms would not be filled out until the mouse is moved. Even if the user was using the mouse, they would not be able to do anything until moving it, but it would still look funny to leave the form blank and then it would suddenly update once the mouse is moved.

I found another thread where it is said that it is not good practice to use global or semi-global variables. I'd expect a class member to qualify as semi-global. It is also said to pass variables to the parent, but I don't think that would apply to my case.

Is there a way to trigger an event when switching interfaces? If not, does anyone have any ideas?

To help clarify, here is part of the classes for keeping track of data (if I pasted the whole thing it would be too long; mostly I want to show how I use members and local variables):
class EditCards(ModelInfo):
    def __init__(self, databaseConnection, userID):
        super().__init__(databaseConnection, userID)
    def resetData(self):
        self.deckID = 0 # ID of deck currently being edited
        self.edited = None # collection of cards that have been edited (to be saved)
        self.original = None # deck of cards without edits in the current session
        self.cardOrder = None # list of card IDs; the order in which cards are browsed
        self.numNewCards = 0 # number of cards inserted during the editing session
        self.numSides = 2 # number of sides of the deck currently being edited
        self.totalCards = 0 # Total number of cards being edited in the deck
    def selectDeckForEditing(self, deckID):
        self.deckID = deckID
        self.original = ReferenceDeck()
        self.edited = ReferenceDeck()
        self.cardOrder = list(self.original.keys())
        self.numSides = self.getNumDeckSides(deckID)
        self.totalCards = self.original.size()

class EditSingleCard(EditCards):
    def resetData(self):
        self.cardNumber = 0 # the card currently being edited, given as an index of cardOrder
        self.hintNumber = [0] * (self.numSides+1) # Number of the hint currently being edited
        self.totalHints = [0] * (self.numSides+1) # Total number of hints for the card currently being edited
        self.currentHints = [[] for i in range(self.numSides+1)] # Hints of the card currently being edited
        self.isTextModified = False
    def selectDeckForEditing(self, deckID):
        if self.totalCards >= 1:
            self.cardNumber = 1
            self.cardNumber = 0

class EditMultiCards(EditCards):
    ROWS = 25
    def resetData(self):
        self.pageNumber = 1 # the page currently being edited
    def selectDeckForEditing(self, deckID):
        self.pageNumber = 1
        self.totalPages = 1 + self.original.size() // self.__class__.ROWS
Here are part of the methods within the View class:
    def editSingleCardGUI(self):
        deckHeadingEntry = tk.StringVar()
        cardNumberVar = tk.StringVar()
        hintNumberVars = [tk.StringVar()]
        layout = tk.Frame(self.mainFrame)
        sideFrame = tk.Frame(layout)
        hintFrame = tk.Frame(layout)
        deckFrame = tk.Frame(layout)
        self.lay["editSingleCard"] = layout
        sideLabels = []
        sideTexts = []
        sideHintTexts = []
        hintFrames = []
        tagsText = tk.Text(layout, height=5, width=40)
        hintsText = tk.Text(hintFrame, height=5, width=40)
        ec = None # Instance of EditSingleCard
    def editMultiCardGUI(self):
        ROWS = EditMultiCards.ROWS
        ec = None
        layout = tk.Frame(self.mainFrame)
        topFrame = tk.Frame(layout)
        bodyFrame = tk.Frame(layout)
        self.lay["editMultiCard"] = layout
        deckNameVar = tk.StringVar()
        pageNumberVar = tk.StringVar()
        rowCheckVars = [tk.BooleanVar() for i in range(ROWS)]
        sideTexts = [[] for i in range(ROWS)]
        hintTexts = [[] for i in range(ROWS)]
        tagTexts = [tk.Text(bodyFrame, height=4, width=15) for i in range(ROWS)]
        sideLabels = [tk.Label(bodyFrame, text="Side 1"), tk.Label(bodyFrame,\
            text="Side 2")]
        rowChecks = [tk.Checkbutton(bodyFrame) for i in range(ROWS)]
        isHintOpen = [True for i in range(ROWS)]
        dividers = []

Top Page

Possibly Related Threads...
Thread Author Replies Views Last Post
  [PyGUI] Switching between two frames using Tkinter jenkins43 1 212 Jul-17-2019, 10:53 AM
Last Post: metulburr
  [PyQt] call a function in another class darktitan 6 685 Jun-22-2019, 03:33 AM
Last Post: darktitan
  [Tkinter] switching frames nick123 2 547 Apr-18-2019, 04:50 PM
Last Post: francisco_neves2020
  Problem with pygame not switching the mouse image Mondaythe1st 6 2,168 Jul-26-2017, 10:53 PM
Last Post: metulburr

Forum Jump:

Users browsing this thread: 1 Guest(s)