'How do I create a menu that execute .py codes from a python diectory dynamically?

I'm new here , and i have a problem in pyqgis 3.16. I want to make a menu list that every time you click its run a .py file (fuction runf) But i want to create by a list of a files in the directory and i need to the code to read every file in the directory that a choose and execute the .py code. But every time its run the runf go to the final of the list and execute the last .py code for every item in the menu. I think it is because the code just execute runf when i click , but how i change the file_path every time i click ? for execute the right code for every item in the menu.

here the fuction for create itens of menu and the runf(that execute the .py code)

def initGui(self):
        self.menu = QMenu('&Name', self.iface.mainWindow())
        self.iface.mainWindow().menuBar().addMenu(self.menu)
        self.icon_path='icon.path'
        self.dirs = os.listdir(path )
        self.total=[]
        self.runfile=[]
        self.file_name=[]
        self.count=0
        count1=0
        for self.file in self.dirs:
            if self.file.endswith(".py"):
                self.runfile.append(os.path.join(path, self.file))
                basename = os.path.basename(str(self.runfile))
                self.file_name.append(os.path.splitext(basename)[0])
                self.action = QAction(self.file_name[self.count])
                self.action.triggered.connect(self.runf)
                self.action.setIcon(QIcon(self.icon_path))
                self.total.append(self.action)
                self.count=self.count+1
        for i in range(len(self.total)):
            self.menu.addAction(self.total[i])
def runf(self):
   with open(self.runfile[self.count],"r") as rnf:
      exec(rnf.read())

PS: sorry by my english



Solution 1:[1]

In your runf method, you always open the "last" file with self.runfile[self.count] because when you finally run the runf method, self.count is for each entry the same.

Change your runf method to:

def runf(self, file_number):
   with open(self.runfile[file_number], "r") as file:
      exec(file.read())

And then call action.triggered.connect using a lambda (but it needs an additional workaround):

def initGui(self):
    # ...
    for self.file in self.dirs:
        if self.file.endswith(".py"):
            # ...
            self.action.triggered.connect(
                lambda file_number=self.count: self.runf(file_number)
            )
            # ...

BTW there is no need to have self.action, self.count and so on as member variables when you can use local variables. You overwrite them with every iteration in your for-loop.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1