omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    Two instances of labels updated by time()

    Pythonista
    6
    15
    8857
    Loading More Posts
    • 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.
    • ccc
      ccc last edited by

      Guys... The use case is a chess timer... Only one of the two timers is running at a time so race conditions should not be a problem.

      Phuket2 1 Reply Last reply Reply Quote 0
      • Phuket2
        Phuket2 @ccc last edited by

        @ccc , yes I did mention that the "timers" would not be running together for chess. That's why I didn't test ui.delays, as I this case it wouldn't matter. A start on timer should stop the other timer.

        1 Reply Last reply Reply Quote 0
        • JonB
          JonB last edited by

          My point was you cannot run two ui.in_backgrounds simultaneously, regardless of if you are only updating one. For chess, I would think there would be just one timer loop, which keeps track of the delta time since the last call, and then subtract from the appropriate variable, and update both.

          1 Reply Last reply Reply Quote 1
          • Vertec
            Vertec last edited by

            Unfortunately for me I'm a little green for yours answers. Been coding for a total of 8 weeks and only in 1hr byte sessions.

            At this point I'm only using the custom gui module and adding references to drag and dropped elements.

            As a beginner I find this manageable and am slowly learning the code as I wrote and rewrite little apps borrowing sections of code from here and the examples.

            I can easily make a digital clock. Or customise the analogue clock. What I can't do is make two and count them backwards.

            Ideally I'd like to reference ui.labels in the module for clock displays simply because it's at "my level" of understanding.

            Thanks again all

            Tizzy 2 Replies Last reply Reply Quote 0
            • Tizzy
              Tizzy @Vertec last edited by

              @Vertec im a little green join the club! :D

              1 Reply Last reply Reply Quote 0
              • Tizzy
                Tizzy @Vertec last edited by Tizzy

                @Vertec @jonb @Webmaster4o @Phuket2 this is my go at it, what do you think?

                UPDATE: streamlined the code a little bit by making there be discreet "active" and "inactive" players, with a currentActivePlayer.otherPlayer attribute pointing to the other player to get rid of some unnecessary conditionals.

                
                import ui,time
                import bz2
                from base64 import b64decode
                
                #versions oldest - newest
                #https://gist.github.com/c61951cc318e7beaf378f6c292f94883
                #https://gist.github.com/b8e6f213d19e9d4668a7f28bbd1efe9f
                #https://gist.github.com/712b62011ad0b76409f8faf77a3add8c
                
                class clockster(object):
                	
                	def __init__(self):
                		
                		#_______PACKAGED UI ______
                		compressedui = '''\
                QlpoOTFBWSZTWeGcOhAAAz3fgFUQUGd/9T+E3Yq/r976QAL82kAisQyQ0mJhNRlT01DQPU
                e1T0mE0AAbUCJRqVP0U09pTygAPUaAPUAMh6TeqYHMAmmATIYAAmCYAAAIpTU9IT9JMm1A
                9Q0AAAAABpSsGxSEROVLA/s9kiUJei8kAkksSaVEhIYQZzK1AQrUBuUGYwQQwLiohYHlmE
                GlLV+A/vRmcjTMwkHNmBgqD+wxhADgXkoDGvhvLCICx2jSuNSNdcDqff28dZk5ENgjuTXu
                TcU2KIxqiwl7wswixLUMsli7ULESKam7yePbJHSF0sl0hAOtEQMBGjSg19jAAvtVAsSRIF
                6DhPl0Q93YzqpQL2wYnXvZj1HPablEQp+AhI0Gyo0QWOAf18kQMJIXEkTvCBCW2N3zUdkw
                aPDyJVBr3ODSrA1Duxob4YBHsjW+gxvdpGDJxIis9Ews2YGwVKBrp7UcXMEmBSICZjuq4x
                tYyWEE2CW5sZWDYCQyZVrJrsgeiwdV7lNmTAnGdIQjGVc0ybkHZNuHEggOP8K3OmNYIKwW
                W2j6Jhjm9Zd5wnlQDOlM52yze50sUc11IPut5YrOlQ1YXR0c68D9ZA6npDWn4HlyV5U+QH
                hx4kd7+76wDeO/hxFEfgNAYqZKUMAnBZzjG0U5nYNyksuOO0feXiaYSI9SX/XTErAqDkM1
                jgBdy7Dm3eoDuLYdelsw8I6BTI6B553ByI5xPM4g5EW00BRhI8B/gXioeDpvLBA3YNnY2U
                J4GhrxPdTb0vE3HB0YgWc3SOo7bWVBiAtccjUoNGgN9SqY2RapMHCN8DImkpc/hkYhChkH
                QZNo4W6JkGU9TGEiBnWFg5akpaPSGBMtzA4AM7iSh8pCe5DBBiw0KX9aeyudSg3nyI8vNY
                nAn3kTrF3JFOFCQ4Zw6EA=
                '''
                		pyui = bz2.decompress(b64decode(compressedui))
                		v = ui.load_view_str(pyui.decode('utf-8'))
                		self.v = v
                		#_______END PACKAGED UI________
                		
                		
                		#self.v = ui.load_view()
                		self.v.present('sheet')
                		
                		print(self.v["playerOne"])
                		
                		self.startTime = time.time()
                		self.gameBegun = False
                		
                		
                		self.playerOne = self.v["playerOne"]
                		self.playerTwo = self.v["playerTwo"]
                		self.playerOne.timeLabel = self.v["playerOneTime"]
                		self.playerTwo.timeLabel = self.v["playerTwoTime"]
                		
                		self.playerOne.totalTimeElapsed = 0
                		self.playerTwo.totalTimeElapsed = 0 
                		self.playerOne.latestTurnElapsed = 0
                		self.playerTwo.latestTurnElapsed = 0
                		
                		self.playerOne.otherPlayer = self.playerTwo
                		self.playerTwo.otherPlayer = self.playerOne
                		
                		
                		self.currentActivePlayer = "NOBODY"
                		
                		
                		self.mainLoop()
                	
                	def togglePlayer(self,sender):
                		print(sender)
                		print(self.currentActivePlayer)
                		if self.currentActivePlayer == sender:
                			print("no change....")
                			pass
                			
                		elif self.currentActivePlayer == "NOBODY":
                			print("this means THE GAME HAS BEGUN!")
                			self.gameBegun = True
                			self.startTime = time.time()
                			self.currentActivePlayer = sender
                			self.currentInactivePlayer = self.currentActivePlayer.otherPlayer
                			
                			self.lastTimeToggled = time.time()
                			print('...first move by: ',sender.name)
                			
                		else:
                			print('currentActivePlayer changing....')
                			#cleaning up old active plyayer
                			self.currentActivePlayer.totalTimeElapsed = self.currentActivePlayer.totalTimeElapsed+self.currentActivePlayer.latestTurnElapsed
                			
                			#new active/inactive player
                			self.currentActivePlayer = sender
                			self.currentInactivePlayer = sender.otherPlayer
                			
                			self.lastTimeToggled = time.time()
                			print("player toggled to",sender.name)
                			
                			
                	def switchClockOnForSelectedPlayer(self):
                		self.currentActivePlayer.latestTurnElapsed= time.time()-self.lastTimeToggled
                		self.currentActivePlayer.timeToDisplay= self.currentActivePlayer.totalTimeElapsed + self.currentActivePlayer.latestTurnElapsed
                		self.currentActivePlayer.timeLabel.text = str(round(self.currentActivePlayer.timeToDisplay))
                		
                		self.totalElapsedTimeBySumming = self.currentActivePlayer.timeToDisplay + self.currentInactivePlayer.totalTimeElapsed
                		
                		
                	@ui.in_background						
                	def mainLoop(self):
                		
                		while self.v.on_screen:
                			
                			if self.gameBegun is True:
                				self.switchClockOnForSelectedPlayer()
                				
                				timeElapsed = round(time.time()-self.startTime,0)
                				print(round(time.time()-self.startTime))
                				#self.v["totalTime"].text = str(timeElapsed)
                				#CHANGED THE ABOVE TO BELOW SO TIMES ADD UP!
                				self.v["totalTime"].text = str(round(self.totalElapsedTimeBySumming))
                				
                			time.sleep(1)
                		
                		
                		
                if __name__ == "__main__":
                	a = clockster()
                	
                
                
                
                Phuket2 1 Reply Last reply Reply Quote 1
                • Phuket2
                  Phuket2 @Tizzy last edited by

                  @Tizzy , I didn't actually run your code. And look maybe I am wrong, the more experienced guys can comment if I am. But with ui Programming, I would have thought it would not be so good to be in a tight loop like you did. My approach was something more akin to a state/stateless machine. But in my mind it will allow the rest of the ui to respond a lot more normally to other events. Eg, the timer is going. Well, it's not really a timer. It's just the difference between 2 points in time and updating a label to show the time elapsed. But, it might be valid to hit a menu item or other ui controls also. In my mind the tight while loop is a over kill for this.
                  Again, I am still learning.

                  1 Reply Last reply Reply Quote 0
                  • JonB
                    JonB last edited by JonB

                    This is a good start.

                    while True with a sleep is a fine pattern in python ( check cpu usage time.clock(), it is the same for a ui.delay based approach and a time.sleep approach). time.sleep gives up control to the os. There needs to be a way to break out of the loop though.

                    It probably should be while self.v.on_screen to stop the main loop when the view is closed.

                    Also, a thread would be safer than in_background, since no other in_background items can run until this one exits-- eventually if this is integrated into a chess game you might need ui.in_background for other reasons, so the mainloop should not be background based. Again, this is just sort of a misnomer of ui.in_background, all in_background calls share a single queue.

                    There is some imprecision in this approach when tapping very quickly -- the individual timers do not add up to the total. I think the main issue was that tapping the button did not update the labels, and the label update routine only updated one set at a time, so you can get into a condition where the labels get out of sync if tapping back and forth.

                    For this type of thing it is important to minimize the number of places time() is called for precision. Also, the action should update the labels, since it might occur on a half second boundary.

                    I made some minor updates:

                    • check self.on_screen to kill mainloop
                    • used a thread rather than in_background for main loop
                    • Minimize calls to time() to ensure everything is consistent. Basicslly once in the action, once in the label update routine. Also update labels whenever a button is pressed so all info is up to date
                      - I formatted the labels to look more like a chess clock.
                      - changed update interval to 0.01 sec, because... it is more exciting
                      - clocks count backwards instead of forward. Also, usually a chess clock pressing your button starts your opponents timer, so i updated that
                      - added sound effects :)

                    https://gist.github.com/6b7dcc13b39440217abfab7a51105d68

                    1 Reply Last reply Reply Quote 0
                    • Tizzy
                      Tizzy last edited by Tizzy

                      @Phuket2 @JonB thanks for the feedback! Lots to think about..

                      To start somewhere -

                      • In order to make sure the total elapsed time always adds up I made it be a summation of each of the player elapsed times. This is probably cheating?
                      • Also I got rid of some conditionals by making discrete active and inactive player objects.
                      • Also added self.v.on_screen as the condition for the while loop.

                      And I meant to ask, too - is it considered good practice to use UI objects as the main objects of your data structure like I did adding attributes onto them willy nilly?

                      1 Reply Last reply Reply Quote 0
                      • JonB
                        JonB last edited by

                        Dunno whether it is good practice or not, but it is darn convienent. i say go for it! It means your action code does not even need to know which player it is

                        If you look at the gist i posted, the times always add up perfectly, because i only call time() once, and make sure to update in the tap action with any fractional times. Also when displaying, be sure to display both sets of labels ( and total time) all using the same time() call.

                        1 Reply Last reply Reply Quote 0
                        • First post
                          Last post
                        Powered by NodeBB Forums | Contributors