Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • 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

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 826 Views
  • 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
    #1

    Hi all
    I'm creating a pyQt5 window to show several boards one after another for displaying shapes for users to click one target shape on each board.

    My question is how to dynamically create 2 to 4 QTimers for shapes based on the "entry_time" attributes in a loaded json file. I also want to start QTimers simultaneously and link them with timeout events.

    Each shape has its own entryTimer AND exitTimer. For example I have 2 boards. The entryTimer is started at the same time for all shapes on both board 1 AND 2. The entrytime value for shapes on board 2 has a value higher than board 1 entrytime + duaration. (duration indicates the time (in seconds) that the shapes appear.)
    So if there are 16 shapes, and 2 boards, there will be a total of 32 entrytimes. The first 16 entrytimes are defined by the entrytime for board_1 and the second 16 entrytimes are defined by the entrytime for board_2 . The second 16 entrytimes values will be higher than the first 16 entrytimes + duration so these shapes only appear after the first 16 shapes are cleared by the exitTimer.

    The exitTimer is started at the same time for all shapes on the SAME board. When it times out, the clearboard() method removes all shapes to make room for shapes on the next board. Additionally, if a user correctly clicks the target shape before the duration expires, the clearboard() method is also called. It updates the next board entrytime to 0.5 seconds for the shapes that appear on the following board to prevent waiting for the remainder of the duration to expire.

    For example, if the duration is set to 60 seconds and a user correctly clicks the target shape in 3 seconds. The clearboard() resets the entrytime to 0.5 seconds to prevent any delays for the shape appearing on the following board.

    Note: clearboard() only reset timers without actually manipulating shapes

    The JSON file may contain 2 to 4 boards, each with its own entry_time attribute applicable to all shapes on the board. Any help is appreciated!
    0a1eaf41-2e67-46a0-84fd-878998fd86fa-image.png

    class MyApp(QMainWindow):
       def __init__(self):
            # some Initialization for the main window 
            self.ui.NextButton.clicked.connect(self.showPage1)
       def showPage1(self):
            file = QFile("./practice_scripts/OUCH_0_1_1_2.json")
            file.open(QFile.ReadOnly | QFile.Text)
            if file.isOpen():
                data = file.readAll()
                qjson = self.jsonParse(data)
                self.ui.startBtn.clicked.connect(lambda: self.showTrial1(qjson))
       def showTrial1(self, qjson):
            #Trial1 has two boards
            boards = qjson["boards"].toArray()
            timerList = []
            for b in boards:
                board = b.toObject()
                timerList.append(board["entry_time"].toDouble())
            for t in timerList:
            #currently I store entry_time into a list and create a QTimer for each item in the list, but I don't know how to start them at the same time and if I need to use threads
                boardEntrytimer = QTimer(self)
                print("BoardEntrytimer Started at  " + getTimestamp())
                boardEntrytimer.singleShot(int(t), self.updateBoard(board))
        def updateBoard(self, board):
            print("updated!  " + getTimestamp())
            bd = Board(board)
    class Board(QWidget):
        def __init__(self, board, parent=None):
            #some initialization of the board
           self.entry_time = board["entry_time"].toInt()
           self.Entrytimer = QTimer(self)
           self.Entrytimer.singleShot(self.entry_time * 1000, self.drawShapes)
           self.Exittimer = QTimer(self)
           print("prepare to clear board  ~ 20s " + getTimestamp())
           self.Exittimer.singleShot(self.duration * 1000, self.clearBoard)
        def drawShapes(self):
        # code to draw shapes on a board
        def clearBoard(self):
        #  code to clear shapes on a board
    
    enjoysmathE 1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      To start your list of timers, the simple solution is to iterate through it and start them all. That should be fast enough for your needs.

      Note that in your code, you seem to create new boards without making anything with them.

      What you should rather do is build all your boards, and then when time comes, iterate through your boards and start the appropriate timers from there. Here you seem to duplicate the timers and then you won't start the right one.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      0
      • H htjane

        Hi all
        I'm creating a pyQt5 window to show several boards one after another for displaying shapes for users to click one target shape on each board.

        My question is how to dynamically create 2 to 4 QTimers for shapes based on the "entry_time" attributes in a loaded json file. I also want to start QTimers simultaneously and link them with timeout events.

        Each shape has its own entryTimer AND exitTimer. For example I have 2 boards. The entryTimer is started at the same time for all shapes on both board 1 AND 2. The entrytime value for shapes on board 2 has a value higher than board 1 entrytime + duaration. (duration indicates the time (in seconds) that the shapes appear.)
        So if there are 16 shapes, and 2 boards, there will be a total of 32 entrytimes. The first 16 entrytimes are defined by the entrytime for board_1 and the second 16 entrytimes are defined by the entrytime for board_2 . The second 16 entrytimes values will be higher than the first 16 entrytimes + duration so these shapes only appear after the first 16 shapes are cleared by the exitTimer.

        The exitTimer is started at the same time for all shapes on the SAME board. When it times out, the clearboard() method removes all shapes to make room for shapes on the next board. Additionally, if a user correctly clicks the target shape before the duration expires, the clearboard() method is also called. It updates the next board entrytime to 0.5 seconds for the shapes that appear on the following board to prevent waiting for the remainder of the duration to expire.

        For example, if the duration is set to 60 seconds and a user correctly clicks the target shape in 3 seconds. The clearboard() resets the entrytime to 0.5 seconds to prevent any delays for the shape appearing on the following board.

        Note: clearboard() only reset timers without actually manipulating shapes

        The JSON file may contain 2 to 4 boards, each with its own entry_time attribute applicable to all shapes on the board. Any help is appreciated!
        0a1eaf41-2e67-46a0-84fd-878998fd86fa-image.png

        class MyApp(QMainWindow):
           def __init__(self):
                # some Initialization for the main window 
                self.ui.NextButton.clicked.connect(self.showPage1)
           def showPage1(self):
                file = QFile("./practice_scripts/OUCH_0_1_1_2.json")
                file.open(QFile.ReadOnly | QFile.Text)
                if file.isOpen():
                    data = file.readAll()
                    qjson = self.jsonParse(data)
                    self.ui.startBtn.clicked.connect(lambda: self.showTrial1(qjson))
           def showTrial1(self, qjson):
                #Trial1 has two boards
                boards = qjson["boards"].toArray()
                timerList = []
                for b in boards:
                    board = b.toObject()
                    timerList.append(board["entry_time"].toDouble())
                for t in timerList:
                #currently I store entry_time into a list and create a QTimer for each item in the list, but I don't know how to start them at the same time and if I need to use threads
                    boardEntrytimer = QTimer(self)
                    print("BoardEntrytimer Started at  " + getTimestamp())
                    boardEntrytimer.singleShot(int(t), self.updateBoard(board))
            def updateBoard(self, board):
                print("updated!  " + getTimestamp())
                bd = Board(board)
        class Board(QWidget):
            def __init__(self, board, parent=None):
                #some initialization of the board
               self.entry_time = board["entry_time"].toInt()
               self.Entrytimer = QTimer(self)
               self.Entrytimer.singleShot(self.entry_time * 1000, self.drawShapes)
               self.Exittimer = QTimer(self)
               print("prepare to clear board  ~ 20s " + getTimestamp())
               self.Exittimer.singleShot(self.duration * 1000, self.clearBoard)
            def drawShapes(self):
            # code to draw shapes on a board
            def clearBoard(self):
            #  code to clear shapes on a board
        
        enjoysmathE Offline
        enjoysmathE Offline
        enjoysmath
        wrote on last edited by
        #3

        @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.

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

        jeremy_kJ 1 Reply Last reply
        0
        • 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 Extensions
                            • Unsolved