Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. how to dynamically create 2 to 4 QTimers based on attributes in a json file
Qt 6.12 LTS is out! See what's new in the release blog

how to dynamically create 2 to 4 QTimers based on attributes in a json file

Scheduled Pinned Locked Moved Unsolved Qt for Python
13 Posts 6 Posters 4.0k Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • H Offline
    H Offline
    htjane
    wrote on last edited by htjane
    #4

    Thank you both @SGaist and @enjoysmath !
    I restructured code as SGaist suggested, created a list of boards and iterated over the board list.
    Like enjoysmath said, I also encountered the issue of only the last board was updated in the singleShot() method. Using a lambda function and grab argument fixed the issue.

    Seems like for entry timers I have a working solution now at least on my machine. Below is my code if anyone would like to take a look: Note that it is not a Minimal, Reproducible Example because my app includes some other functions and event handlers.

        def showTrial1(self, qjson):
            self.ui.stackedWidget.setCurrentWidget(self.ui.trial1)
            rulesets = qjson["rulesets"].toObject()
            boardArr = qjson["boards"].toArray()
            for b in boardArr:
                board = b.toObject()
                bd = Board(rulesets, board, self.geometry())
                self.boards.append(bd)
                self.ui.stackedWidget.addWidget(bd)
                
            for bd in self.boards:
                boardEntrytimer = QTimer(self)
                boardEntrytimer.singleShot(int(bd.entry_time)*1000, lambda arg = bd: self.createBoard(arg))
    
        def createBoard(self,board):
            # print("updated!  " + getTimestamp())
           self.ui.stackedWidget.setCurrentWidget(board)
    
    enjoysmathE jeremy_kJ 4 Replies Last reply
    1
    • H htjane

      Thank you both @SGaist and @enjoysmath !
      I restructured code as SGaist suggested, created a list of boards and iterated over the board list.
      Like enjoysmath said, I also encountered the issue of only the last board was updated in the singleShot() method. Using a lambda function and grab argument fixed the issue.

      Seems like for entry timers I have a working solution now at least on my machine. Below is my code if anyone would like to take a look: Note that it is not a Minimal, Reproducible Example because my app includes some other functions and event handlers.

          def showTrial1(self, qjson):
              self.ui.stackedWidget.setCurrentWidget(self.ui.trial1)
              rulesets = qjson["rulesets"].toObject()
              boardArr = qjson["boards"].toArray()
              for b in boardArr:
                  board = b.toObject()
                  bd = Board(rulesets, board, self.geometry())
                  self.boards.append(bd)
                  self.ui.stackedWidget.addWidget(bd)
                  
              for bd in self.boards:
                  boardEntrytimer = QTimer(self)
                  boardEntrytimer.singleShot(int(bd.entry_time)*1000, lambda arg = bd: self.createBoard(arg))
      
          def createBoard(self,board):
              # print("updated!  " + getTimestamp())
             self.ui.stackedWidget.setCurrentWidget(board)
      
      enjoysmathE Offline
      enjoysmathE Offline
      enjoysmath
      wrote on last edited by
      #5

      @htjane https://stackoverflow.com/a/7546960/7076615

      https://github.com/enjoysmath
      https://math.stackexchange.com/users/26327/exercisingmathematician

      1 Reply Last reply
      0
      • H htjane

        Thank you both @SGaist and @enjoysmath !
        I restructured code as SGaist suggested, created a list of boards and iterated over the board list.
        Like enjoysmath said, I also encountered the issue of only the last board was updated in the singleShot() method. Using a lambda function and grab argument fixed the issue.

        Seems like for entry timers I have a working solution now at least on my machine. Below is my code if anyone would like to take a look: Note that it is not a Minimal, Reproducible Example because my app includes some other functions and event handlers.

            def showTrial1(self, qjson):
                self.ui.stackedWidget.setCurrentWidget(self.ui.trial1)
                rulesets = qjson["rulesets"].toObject()
                boardArr = qjson["boards"].toArray()
                for b in boardArr:
                    board = b.toObject()
                    bd = Board(rulesets, board, self.geometry())
                    self.boards.append(bd)
                    self.ui.stackedWidget.addWidget(bd)
                    
                for bd in self.boards:
                    boardEntrytimer = QTimer(self)
                    boardEntrytimer.singleShot(int(bd.entry_time)*1000, lambda arg = bd: self.createBoard(arg))
        
            def createBoard(self,board):
                # print("updated!  " + getTimestamp())
               self.ui.stackedWidget.setCurrentWidget(board)
        
        enjoysmathE Offline
        enjoysmathE Offline
        enjoysmath
        wrote on last edited by
        #6

        @htjane I think ๐Ÿ’ญthat u're already doing it correctly โœ”. However, please tell us more about your bug. ๐Ÿ›

        https://github.com/enjoysmath
        https://math.stackexchange.com/users/26327/exercisingmathematician

        1 Reply Last reply
        0
        • H htjane

          Thank you both @SGaist and @enjoysmath !
          I restructured code as SGaist suggested, created a list of boards and iterated over the board list.
          Like enjoysmath said, I also encountered the issue of only the last board was updated in the singleShot() method. Using a lambda function and grab argument fixed the issue.

          Seems like for entry timers I have a working solution now at least on my machine. Below is my code if anyone would like to take a look: Note that it is not a Minimal, Reproducible Example because my app includes some other functions and event handlers.

              def showTrial1(self, qjson):
                  self.ui.stackedWidget.setCurrentWidget(self.ui.trial1)
                  rulesets = qjson["rulesets"].toObject()
                  boardArr = qjson["boards"].toArray()
                  for b in boardArr:
                      board = b.toObject()
                      bd = Board(rulesets, board, self.geometry())
                      self.boards.append(bd)
                      self.ui.stackedWidget.addWidget(bd)
                      
                  for bd in self.boards:
                      boardEntrytimer = QTimer(self)
                      boardEntrytimer.singleShot(int(bd.entry_time)*1000, lambda arg = bd: self.createBoard(arg))
          
              def createBoard(self,board):
                  # print("updated!  " + getTimestamp())
                 self.ui.stackedWidget.setCurrentWidget(board)
          
          enjoysmathE Offline
          enjoysmathE Offline
          enjoysmath
          wrote on last edited by
          #7

          @htjane Oh I see, you've already solved it. Told you 95% chance.

          https://github.com/enjoysmath
          https://math.stackexchange.com/users/26327/exercisingmathematician

          1 Reply Last reply
          0
          • enjoysmathE enjoysmath

            @htjane

            Be careful creating signal/slot connections in a Loop. At least in PyQt5 I always ran into the bug where all were connected to the last slot or something. Solved by more explicit lambda function capture parameters or something.

            There's a 95% chance you'll also hit this bug.

            jeremy_kJ Offline
            jeremy_kJ Offline
            jeremy_k
            wrote on last edited by jeremy_k
            #8

            @enjoysmath said in how to dynamically create 2 to 4 QTimers based on attributes in a json file:

            @htjane

            Be careful creating signal/slot connections in a Loop. At least in PyQt5 I always ran into the bug where all were connected to the last slot or something. Solved by more explicit lambda function capture parameters or something.

            That's unrelated to PyQt, and not a bug. Captures bind to variables, not the value they hold at the time of capture.

            i = 0
            function = lambda: print(i)
            i = 1
            function()
            

            Output:

            1
            

            Asking a question about code? http://eel.is/iso-c++/testcase/

            JonBJ 1 Reply Last reply
            1
            • H htjane

              Thank you both @SGaist and @enjoysmath !
              I restructured code as SGaist suggested, created a list of boards and iterated over the board list.
              Like enjoysmath said, I also encountered the issue of only the last board was updated in the singleShot() method. Using a lambda function and grab argument fixed the issue.

              Seems like for entry timers I have a working solution now at least on my machine. Below is my code if anyone would like to take a look: Note that it is not a Minimal, Reproducible Example because my app includes some other functions and event handlers.

                  def showTrial1(self, qjson):
                      self.ui.stackedWidget.setCurrentWidget(self.ui.trial1)
                      rulesets = qjson["rulesets"].toObject()
                      boardArr = qjson["boards"].toArray()
                      for b in boardArr:
                          board = b.toObject()
                          bd = Board(rulesets, board, self.geometry())
                          self.boards.append(bd)
                          self.ui.stackedWidget.addWidget(bd)
                          
                      for bd in self.boards:
                          boardEntrytimer = QTimer(self)
                          boardEntrytimer.singleShot(int(bd.entry_time)*1000, lambda arg = bd: self.createBoard(arg))
              
                  def createBoard(self,board):
                      # print("updated!  " + getTimestamp())
                     self.ui.stackedWidget.setCurrentWidget(board)
              
              jeremy_kJ Offline
              jeremy_kJ Offline
              jeremy_k
              wrote on last edited by
              #9

              @htjane said in how to dynamically create 2 to 4 QTimers based on attributes in a json file:

              Thank you both @SGaist and @enjoysmath !
              I restructured code as SGaist suggested, created a list of boards and iterated over the board list.
              Like enjoysmath said, I also encountered the issue of only the last board was updated in the singleShot() method. Using a lambda function and grab argument fixed the issue.

              Seems like for entry timers I have a working solution now at least on my machine. Below is my code if anyone would like to take a look: Note that it is not a Minimal, Reproducible Example because my app includes some other functions and event handlers.

                      for bd in self.boards:
                          boardEntrytimer = QTimer(self)
                          boardEntrytimer.singleShot(int(bd.entry_time)*1000, lambda arg = > ```
              

              This code creates an extra QTimer on each loop iteration. QTimer.singleShot() is a static function.

              Asking a question about code? http://eel.is/iso-c++/testcase/

              1 Reply Last reply
              1
              • jeremy_kJ jeremy_k

                @enjoysmath said in how to dynamically create 2 to 4 QTimers based on attributes in a json file:

                @htjane

                Be careful creating signal/slot connections in a Loop. At least in PyQt5 I always ran into the bug where all were connected to the last slot or something. Solved by more explicit lambda function capture parameters or something.

                That's unrelated to PyQt, and not a bug. Captures bind to variables, not the value they hold at the time of capture.

                i = 0
                function = lambda: print(i)
                i = 1
                function()
                

                Output:

                1
                
                JonBJ Offline
                JonBJ Offline
                JonB
                wrote on last edited by JonB
                #10

                @jeremy_k said in how to dynamically create 2 to 4 QTimers based on attributes in a json file:

                That's unrelated to PyQt, and not a bug.

                @enjoysmath will be thinking of is the way Python (nothing to do with PyQt, and agreed not a "bug") works for the following pattern:

                for i in range(10):
                    list_of_buttons[i].clicked.connect(lambda: print(i))
                

                I and others started out writing this (posts in this forum and stackoverflow), expecting it have the lambda-slot tell us which button was clicked. Like:

                for (int i = 0; i < 10; i++)
                    connect(this, list_of_buttons[i], [i]() { qDebug() << i; });
                

                which is what people expect it to do.

                I guess Python passes something like &i would do in C++. The Python user needs:

                    list_of_buttons[i].clicked.connect(lambda ii=i: print(ii))
                

                which people do not realise at first, and I imagine @enjoysmath has this in mind.

                1 Reply Last reply
                1
                • H Offline
                  H Offline
                  htjane
                  wrote on last edited by
                  #11

                  Hi all,
                  Sorry I'm new to Qt. Here again asking a follow-up question about reading board instance attributes that have just been modified by keypress event. Not sure if I should post it here or open a new thread. Please Lmk.

                  My question is in the KeyPressEvent of Board class, I set the self.finish attribute to True to indicate the target shape has been clicked and a key pressed, then in MyApp class I want to get the finish attribute of board instance using an if-condition to check if the board has been finished. But the code in MyApp executes before "finish" has been modified by KeyPressEvent, without hanging or waiting for it and "finish" is False whenever I print it in MyApp class. Specifically, I want to read "finish" of each board in MyApp and if it's True, set the entry_time of the next board to 0.5s to prevent waiting this board's duration to expire. Could you guys suggest some solutions?

                  class MyApp(QMainWindow):
                  # constructor and some functions...
                    def showTrial1(self, qjson):
                          self.ui.stackedWidget.setCurrentWidget(self.ui.trial1)
                  
                          rulesets = qjson["rulesets"].toObject()
                          boardArr = qjson["boards"].toArray()
                  
                          for b in boardArr:  # create boards
                              board = b.toObject()
                              bd = Board(rulesets, board, self.geometry())
                              self.boards.append(bd)
                              self.ui.stackedWidget.addWidget(bd)
                  
                          for bd in self.boards:
                              #call a static timer to draw board based on entry_time
                              QTimer.singleShot(int(bd.entry_time) * 1000, lambda arg=bd: self.createBoard(arg))
                  
                      def createBoard(self, board):
                          board.drawShapes()
                          self.ui.stackedWidget.setCurrentWidget(board)
                  
                  class Board(QWidget):
                      def __init__(self, rulesets, board, size, parent=None):
                          super().__init__(parent)
                          self.rules = rulesets
                          self.lastLeftPoint = QPoint(0, 0)
                          self.id = board["id"].toInt()
                          self.duration = 10  # board["duration"]
                          self.entry_time = board["entry_time"].toDouble()
                          self.stimuli = board["stimuli"]
                          self.shapes = []
                          self.click = False
                          self.boardSize = size
                          self.timeout = False
                          self.finish = False
                  
                      def drawShapes(self):
                          stiArr = self.stimuli.toArray()
                  
                          for s in stiArr:
                  
                              stimuli = s.toObject()
                  
                              ID = stimuli["id"].toInt()
                              # print(ID)
                              color = QtGui.QColor(stimuli["color"].toObject()["color"].toString())
                              text = stimuli["text"].toString()
                              x = math.floor(
                                  stimuli[
                                      "column"].toInt() * self.boardSize.width() / 12.0 - self.boardSize.width() / 24.0)  # 3862 * 2122
                              y = math.floor(stimuli["row"].toInt() * self.boardSize.height() / 12.0 - self.boardSize.height() / 24.0)
                              pos = QtCore.QPoint(x, y)
                              target = stimuli["target"]
                              response = stimuli["response"]
                              # hue = clr["hue"].toInt()
                              # saturation = clr["saturation"].toInt() * 255
                              # value = clr["value"].toInt() * 255
                              # alpha = clr["alpha"].toInt() * 255
                              if stimuli["shape"] == "circle":
                                  self.shapes.append(Circle(ID, 30, pos, color, text, target, response))
                              elif stimuli["shape"] == "cross":
                                  self.shapes.append(Cross(ID, 30, pos, color, text, target, response))
                              elif stimuli["shape"] == "star":
                                  self.shapes.append(Star(ID, 30, pos, color, text, target, response))
                              elif stimuli["shape"] == "hexagon":
                                  self.shapes.append(Hexagon(ID, 30, pos, color, text, target, response))
                              elif stimuli["shape"] == "octagon":
                                  self.shapes.append(Octagon(ID, 30, pos, color, text, target, response))
                              elif stimuli["shape"] == "pentagon":
                                  self.shapes.append(Pentagon(ID, 30, pos, color, text, target, response))
                          # self.update()
                  
                      def paintEvent(self, event):
                          painter = QPainter(self)
                          painter.setRenderHint(QPainter.Antialiasing)
                          if self.timeout:
                              print("cleared!  " + getTimestamp())
                              painter.eraseRect(0, 0, self.boardSize.width(), self.boardSize.height())
                              return
                          for shape in self.shapes:
                              shape.paint(painter)
                          if self.click:
                              painter.setPen(QPen(QColor("green"), 4, Qt.SolidLine))
                              painter.drawRect(self.lastLeftPoint.x() - 15, self.lastLeftPoint.y() - 15, 30, 30)
                              painter.end()
                  
                      def mousePressEvent(self, event):
                          # on left mouse click, it returns the coordinates
                          if event.button() == Qt.LeftButton:
                  
                              self.lastLeftPoint = event.pos()
                              for shape in self.shapes:  # iterate through shapes
                                  if shape.position.x() - 15 <= self.lastLeftPoint.x() <= shape.position.x() + 15 and shape.position.y() - 15 <= self.lastLeftPoint.y() <= shape.position.y() + 15:
                  
                                      if shape.target.toBool():  # if is target
                                          print("clicked the target!")
                                          self.click = True  # set click to true
                                      else:
                                          print("not this one")
                                      self.update()
                  
                      def keyPressEvent(self, e):
                          rules = self.rules["responserules"].toArray()  # get response rules of the board
                          if self.click:
                              if "any" == rules[0].toString() and type(e.key()) is int:  # if rule is any and pressed any key
                                  self.finish = True
                                  print("updated") # verified that this is hit by printing statement
                              else:
                                  myList = [Qt.Key_Q, Qt.Key_W, Qt.Key_E, Qt.Key_R, Qt.Key_T]
                                  if e.text() in myList:
                                      print("In My List")  # if clicked a key in the set
                  
                  
                  jsulmJ 1 Reply Last reply
                  0
                  • H htjane

                    Hi all,
                    Sorry I'm new to Qt. Here again asking a follow-up question about reading board instance attributes that have just been modified by keypress event. Not sure if I should post it here or open a new thread. Please Lmk.

                    My question is in the KeyPressEvent of Board class, I set the self.finish attribute to True to indicate the target shape has been clicked and a key pressed, then in MyApp class I want to get the finish attribute of board instance using an if-condition to check if the board has been finished. But the code in MyApp executes before "finish" has been modified by KeyPressEvent, without hanging or waiting for it and "finish" is False whenever I print it in MyApp class. Specifically, I want to read "finish" of each board in MyApp and if it's True, set the entry_time of the next board to 0.5s to prevent waiting this board's duration to expire. Could you guys suggest some solutions?

                    class MyApp(QMainWindow):
                    # constructor and some functions...
                      def showTrial1(self, qjson):
                            self.ui.stackedWidget.setCurrentWidget(self.ui.trial1)
                    
                            rulesets = qjson["rulesets"].toObject()
                            boardArr = qjson["boards"].toArray()
                    
                            for b in boardArr:  # create boards
                                board = b.toObject()
                                bd = Board(rulesets, board, self.geometry())
                                self.boards.append(bd)
                                self.ui.stackedWidget.addWidget(bd)
                    
                            for bd in self.boards:
                                #call a static timer to draw board based on entry_time
                                QTimer.singleShot(int(bd.entry_time) * 1000, lambda arg=bd: self.createBoard(arg))
                    
                        def createBoard(self, board):
                            board.drawShapes()
                            self.ui.stackedWidget.setCurrentWidget(board)
                    
                    class Board(QWidget):
                        def __init__(self, rulesets, board, size, parent=None):
                            super().__init__(parent)
                            self.rules = rulesets
                            self.lastLeftPoint = QPoint(0, 0)
                            self.id = board["id"].toInt()
                            self.duration = 10  # board["duration"]
                            self.entry_time = board["entry_time"].toDouble()
                            self.stimuli = board["stimuli"]
                            self.shapes = []
                            self.click = False
                            self.boardSize = size
                            self.timeout = False
                            self.finish = False
                    
                        def drawShapes(self):
                            stiArr = self.stimuli.toArray()
                    
                            for s in stiArr:
                    
                                stimuli = s.toObject()
                    
                                ID = stimuli["id"].toInt()
                                # print(ID)
                                color = QtGui.QColor(stimuli["color"].toObject()["color"].toString())
                                text = stimuli["text"].toString()
                                x = math.floor(
                                    stimuli[
                                        "column"].toInt() * self.boardSize.width() / 12.0 - self.boardSize.width() / 24.0)  # 3862 * 2122
                                y = math.floor(stimuli["row"].toInt() * self.boardSize.height() / 12.0 - self.boardSize.height() / 24.0)
                                pos = QtCore.QPoint(x, y)
                                target = stimuli["target"]
                                response = stimuli["response"]
                                # hue = clr["hue"].toInt()
                                # saturation = clr["saturation"].toInt() * 255
                                # value = clr["value"].toInt() * 255
                                # alpha = clr["alpha"].toInt() * 255
                                if stimuli["shape"] == "circle":
                                    self.shapes.append(Circle(ID, 30, pos, color, text, target, response))
                                elif stimuli["shape"] == "cross":
                                    self.shapes.append(Cross(ID, 30, pos, color, text, target, response))
                                elif stimuli["shape"] == "star":
                                    self.shapes.append(Star(ID, 30, pos, color, text, target, response))
                                elif stimuli["shape"] == "hexagon":
                                    self.shapes.append(Hexagon(ID, 30, pos, color, text, target, response))
                                elif stimuli["shape"] == "octagon":
                                    self.shapes.append(Octagon(ID, 30, pos, color, text, target, response))
                                elif stimuli["shape"] == "pentagon":
                                    self.shapes.append(Pentagon(ID, 30, pos, color, text, target, response))
                            # self.update()
                    
                        def paintEvent(self, event):
                            painter = QPainter(self)
                            painter.setRenderHint(QPainter.Antialiasing)
                            if self.timeout:
                                print("cleared!  " + getTimestamp())
                                painter.eraseRect(0, 0, self.boardSize.width(), self.boardSize.height())
                                return
                            for shape in self.shapes:
                                shape.paint(painter)
                            if self.click:
                                painter.setPen(QPen(QColor("green"), 4, Qt.SolidLine))
                                painter.drawRect(self.lastLeftPoint.x() - 15, self.lastLeftPoint.y() - 15, 30, 30)
                                painter.end()
                    
                        def mousePressEvent(self, event):
                            # on left mouse click, it returns the coordinates
                            if event.button() == Qt.LeftButton:
                    
                                self.lastLeftPoint = event.pos()
                                for shape in self.shapes:  # iterate through shapes
                                    if shape.position.x() - 15 <= self.lastLeftPoint.x() <= shape.position.x() + 15 and shape.position.y() - 15 <= self.lastLeftPoint.y() <= shape.position.y() + 15:
                    
                                        if shape.target.toBool():  # if is target
                                            print("clicked the target!")
                                            self.click = True  # set click to true
                                        else:
                                            print("not this one")
                                        self.update()
                    
                        def keyPressEvent(self, e):
                            rules = self.rules["responserules"].toArray()  # get response rules of the board
                            if self.click:
                                if "any" == rules[0].toString() and type(e.key()) is int:  # if rule is any and pressed any key
                                    self.finish = True
                                    print("updated") # verified that this is hit by printing statement
                                else:
                                    myList = [Qt.Key_Q, Qt.Key_W, Qt.Key_E, Qt.Key_R, Qt.Key_T]
                                    if e.text() in myList:
                                        print("In My List")  # if clicked a key in the set
                    
                    
                    jsulmJ Offline
                    jsulmJ Offline
                    jsulm
                    Lifetime Qt Champion
                    wrote on last edited by
                    #12

                    @htjane Why don't you simply emit a "finished" signal to notify others that it is finished? This is how Qt applications (or any other event driven applications) work.

                    https://forum.qt.io/topic/113070/qt-code-of-conduct

                    H 1 Reply Last reply
                    1
                    • jsulmJ jsulm

                      @htjane Why don't you simply emit a "finished" signal to notify others that it is finished? This is how Qt applications (or any other event driven applications) work.

                      H Offline
                      H Offline
                      htjane
                      wrote on last edited by htjane
                      #13

                      @jsulm Thanks! I used signal and slot and now they work well!

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt
                      • Unsolved