0

I am using a for loop to layout a 3 x 3 grid of buttons in the create_widgets() function.

I am having trouble linking each specific button to a link in the 'link_list' (in the dunder init method).

In my code, it (understandably) tries to index the list_list based on the variable "button_number" which will always be out of range because it adds 1 every-time, and upon completion, that puts it out of range. I am seeking help to build a solution where either

  1. the button's command dynamically indexes/slices the list based on the number/location of the button OR
  2. The button's command is concretized with the webbrowser.open() function and the link it matches up with in the index upon creation

I'm also not sure if one is more advantageous than the other.

(FYI the goal: I am building a personal command center to open both Desktop apps and web links from one location & I figure a list is a good starting point to gather important links that I'd like for it to open.)

Thanks so very much for considering helping

Below is my program this far:

import tkinter as tk
import os
import random
import webbrowser
from tkinter import Frame
class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)
        self.master = master
        self.grid()
        self.create_widgets()
        self.link_list = ['evernote:///view/21614375/s192/10b4247b-5f09-4b66-953d-02e5f27aac3c/10b4247b-5f09-4b66-953d-02e5f27aac3c/',
                        'Anaconda-Navigator://', 'zoom.us://', 'Evernote://', 'Messages://',
                        'https://open.spotify.com/track/2gI9RfuSwulsBujm7hbOFv?si=891addaca9524eaf',
                        'spotify://', 'workflowy://', 'Brave-Browser://']

    def create_widgets(self):
        
        self.button_number = 0
        #for loop button creation
        for i in range(3):
            for j in range(3):
                #first button
                self.first_button = tk.Button(self, state = 'normal', padx =50, pady = 50)
                self.first_button["text"] = "BUTTON",self.button_number
                self.first_button["command"] = lambda: webbrowser.open(self.link_list[self.button_number])
                #self.first_button["command"] = self.lets_go
                self.first_button.grid(row = i, column = j)
                self.button_number += 1

        ##QUIT BUTTON
        self.quit = tk.Button(self, text="QUIT", fg="red",
                              command=self.master.destroy)
        self.quit.grid()

    #for First Button
    def lets_go(self):
        webbrowser.open(self.link_list[self.button_number])   #, new = 0 )


root = tk.Tk()
app = Application(master = root)
app.mainloop()
SuperStormer
  • 4,997
  • 5
  • 25
  • 35
  • btw usually `mainloop()` is called on `Tk()` instance not frame – Matiiss May 13 '21 at 14:35
  • Thank you very much @Matiiss -- do you know why that is? – Sam Katz May 18 '21 at 05:00
  • I actually don't know, theoretically it is possible to call `mainloop()` alone but that is bad practice. Probably the reason is that the `Tk()` instance is the main instance on which everything else is based so it would make sense to call `.mainloop()` on the main instance. – Matiiss May 18 '21 at 09:38

1 Answers1

0

Here is a way to do this:

from tkinter import Tk, Button
import webbrowser
from functools import partial

link_list = ['evernote:///view/21614375/s192/10b4247b-5f09-4b66-953d-02e5f27aac3c/10b4247b-5f09-4b66-953d-02e5f27aac3c/',
             'Anaconda-Navigator://', 'zoom.us://', 'Evernote://', 'Messages://',
             'https://open.spotify.com/track/2gI9RfuSwulsBujm7hbOFv?si=891addaca9524eaf',
             'spotify://', 'workflowy://', 'Brave-Browser://']

root = Tk()

row = 0
col = 0
for index, link in enumerate(link_list):
    Button(root, text=f'Button {index + 1}', command=partial(webbrowser.open, link)).grid(row=row, column=col)
    col += 1
    if col >= 3:
        col = 0
        row += 1

root.mainloop()

Basically what partial() does is always executes the given function with the same given arguments every time or at least that would be the best explanation I could give for this case.

About partial()
Other source for partial()

Matiiss
  • 5,970
  • 2
  • 12
  • 29
  • Wow! This is so awesome! I'm so thankful you took the time to show and explain the partial() funtion Also, I like how you navigated mapping the buttons, no matter how many are in link_list, it's always going to come out nice – Sam Katz May 13 '21 at 21:24
  • Thanks, btw I added a few sources that could possibly explain `partial()` a bit better. – Matiiss May 13 '21 at 21:38
  • Rock on! This is such a great function, wow. Thanks for putting in this effort, what a great community we have – Sam Katz May 18 '21 at 05:02
  • btw I would have to mention tho, that as far as I know in this case it would be possible to use `command=lambda link=link: webbrowser.open(link)` too and it should work the same way, it is just that I like using `partial()` more and in my opinion it is more readable, especially if a lot of arguments have to be used, but it has other uses too, I think, don't really know because I use just for this. – Matiiss May 18 '21 at 09:40