Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to use Thread() ?
#1
Hello,

By any luck someone could tell me how i could fix my thread problem.

In the while loop at the begging there is a thread. Its link to a function delai and sub function decompte or (counter in english)
While the counter is in action nothing on my board will be of any use its lock.

The thread() should let the other function run without disrupting the rest. The counter with the thread become eratic. Anyone know how to use the thread in that context?

Thank you

"""

"""
from time import sleep
from threading import Thread
import time
import smtplib
import RPi.GPIO as GPIO
import pygame
from pygame.locals import *
import sys
import time

#Pygame code

clock = pygame.time.Clock()
pygame.init()
pygame.display.set_caption('Panneau de contrôle')
FPS_CLOCK = pygame.time.Clock()

BLANC = (255, 255, 255)
ROUGE = (255, 0, 0)
GREEN = (0, 255, 0)
GRIS = (200, 200, 200)
NOIR = (0, 0, 0)
JAUNE = (255, 255, 0)

screen = pygame.display.set_mode([700, 500])


sysfont = pygame.font.get_default_font()
font = pygame.font.SysFont(None, 48)

def bouton_A(screen, pos, texte):
    rayon = 55
    pygame.draw.circle(screen, GREEN, pos, rayon)
    txtBtn = font.render(texte, True, NOIR)
    rectBtn = txtBtn.get_rect()
    rectBtn.center = (pos)
    screen.blit(txtBtn, rectBtn)
    
def bouton_D(screen, pos, texte):
    rayon = 55
    pygame.draw.circle(screen, ROUGE, pos, rayon)
    txtBtn = font.render(texte, True, NOIR)
    rectBtn = txtBtn.get_rect()
    rectBtn.center = (pos)
    screen.blit(txtBtn, rectBtn)
    
def bouton_nb(screen, pos, texte):
    rayon = 25
    pygame.draw.circle(screen, GRIS, pos, rayon)
    txtBtn = font.render(texte, True, NOIR)
    rectBtn = txtBtn.get_rect()
    rectBtn.center = (pos)
    screen.blit(txtBtn, rectBtn)
    
class Etat():

    def horloge(self):
        theFont=pygame.font.Font(None,42)
       
        theTime=time.strftime("%H:%M:%S", time.localtime())
        timeText=theFont.render(str(theTime), True,(255,255,255),(0,0,0))
        screen.blit(timeText, (448,428))
        pygame.display.update()


    def image(self):
        image = pygame.image.load('3.png')
        screen.blit(image, (110, 406))
        
        
    def Alert_image(self):
        image = pygame.image.load('Master-Warning-Light.png')
        image = pygame.transform.scale(image, (96, 96))
        screen.blit(image, (600, -10))
        #pygame.display.update()
        img_intrus = font.render("Alerte Intrus", True, JAUNE)
        screen.blit(img_intrus, (450, 20))
    
sysfont = pygame.font.get_default_font()
font = pygame.font.SysFont(None, 36)


#Rapsberry Pi code




GPIO.setmode(GPIO.BOARD)
senseur_Del2 = 12 
DEL1 = 18 
DEL2 = 22 
GPIO.setwarnings(False)
GPIO.setup(senseur_Del2,GPIO.IN,pull_up_down=GPIO.PUD_UP) 
GPIO.setup(DEL1,GPIO.OUT,) 
GPIO.setup(DEL2,GPIO.OUT) 
led_off1=False
led_off2=False


def decompte(t):
    while t:
            min , sec = divmod(t,60)
            timer = '{:02d}:{:02d}'.format(min, sec)
            print(timer, end='\r')
            time.sleep(1)
            t -= 1
    if t == 0:
        print('Délai terminé!')
    
            
t = 10            

class Etat_ent:

    def delai(self): #délai d'entrée
            print('Vous avez 10 secondes avant l`enclenchement de l`alarme!')
            GPIO.output(DEL1,True)
            time.sleep(.1)
            decompte(int(t))
            GPIO.output(DEL1,True)
            time.sleep(1)
            GPIO.output(DEL1,False)
            time.sleep(1)
            GPIO.output(DEL1,True)
        
ent =  Etat_ent()       
e = Etat()
while(True):
        
    if led_off1==False and GPIO.input(senseur_Del2)==1:
                        thread = Thread(target=ent.delai, args=())   <-----maybe at the wrong place or stop at the wrong time
                        thread.start()
                        GPIO.output(DEL1,True)
                        time.sleep(.2)
                        GPIO.output(DEL1,False)
                        time.sleep(.2)
                        led_off2=True
                        sleep(.5)
    if GPIO.input(senseur_Del2)==1:
                        GPIO.output(DEL2,True)
                        led_off2=True
                        e.Alert_image()
                        sleep(.5)           

            
   
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            
        x , y = pygame.mouse.get_pos()
        #liste des boutons cliqués
        Bouton_A = x > 104 and x < 204 and y > 196 and y < 301
        Bouton_D = x < 549 and x > 444 and y > 192 and y < 309
        Chiffre_1 = x > 248  and x < 285 and y > 182 and y < 218
        Chiffre_2 = x > 301  and x < 339 and y > 182 and y < 220
        Chiffre_3 = x > 358  and x < 390 and y > 180 and y < 218
        Chiffre_4 = x > 244  and x < 282 and y > 237 and y < 269
        Chiffre_5 = x > 303  and x < 341 and y > 235 and y < 268
        Chiffre_6 = x > 357  and x < 394 and y > 238 and y < 288
        Chiffre_7 = x > 241  and x < 287 and y > 294 and y < 329
        Chiffre_8 = x > 301  and x < 339 and y > 295 and y < 330
        Chiffre_9 = x > 353  and x < 395 and y > 291 and y < 327
        if event.type == pygame.MOUSEBUTTONDOWN:
            
            #print(x,y)
            if Bouton_A:
               img = font.render("Entrez votre code pour désarmer le systeme d'alarme!", True, NOIR)
               screen.blit(img, (20, 100))
               img = font.render("Veuillez entrer votre code pour armer le systeme d'alarme!", True, BLANC)
               screen.blit(img, (20, 100))
               GPIO.output(DEL1,True)
               led_off1=True 
               sleep(.5)
               
            else:
               screen.fill(NOIR) 
               pygame.draw.rect(screen, NOIR, (21, 75, 677, 51), 2)
               GPIO.output(DEL1,False)
               led_off1= False
               sleep(.5)
              
               
            if Bouton_D:
               img = font.render("Entrez votre code pour désarmer le systeme d'alarme!", True, BLANC)
               screen.blit(img, (20, 100))
               GPIO.output(DEL1,False)
               led_off1=False 
               sleep(.5)
               GPIO.output(DEL2,False)
               led_off2=False 
               sleep(.5)
               thread.stop()
            
                
            if Chiffre_1:
               img = font.render("*", True, BLANC)
               screen.blit(img, (258, 131))
            if Chiffre_2:
               img = font.render("*", True, BLANC)
               screen.blit(img, (298, 131))
            if Chiffre_3:
               img = font.render("*", True, BLANC)
               screen.blit(img, (335, 131))
            if Chiffre_4:
               img = font.render("*", True, BLANC)
               screen.blit(img, (370, 131))
               screen.fill(NOIR) 
               pygame.draw.rect(screen, NOIR, (21, 125, 677, 51), 2)
               img = font.render("Merci", True, BLANC)
               screen.blit(img, (295, 350))
               
            if Chiffre_5:
               img = font.render("*", True, BLANC)
               screen.blit(img, (258, 131))
               e.Alert_image()
               GPIO.output(DEL2,True)
               led_off1=True 
               sleep(.5)
            if Chiffre_6:
               img = font.render("*", True, BLANC)
               screen.blit(img, (258, 131))
               e.Alert_image()
               GPIO.output(DEL2,True)
               led_off1=True 
               sleep(.5)
            if Chiffre_7:
               img = font.render("*", True, BLANC)
               screen.blit(img, (258, 131))
               e.Alert_image()
               GPIO.output(DEL2,True)
               led_off1=True 
               sleep(.5)
            if Chiffre_8:
               img = font.render("*", True, BLANC)
               screen.blit(img, (258, 131))
               e.Alert_image()
               GPIO.output(DEL2,True)
               led_off1=True 
               sleep(.5)
            if Chiffre_9:
               img = font.render("*", True, BLANC)
               screen.blit(img, (258, 131))
               e.Alert_image()
               GPIO.output(DEL2,True)
               led_off1=True 
               sleep(.5)
             
            
                 
                 
                 
    #listes des boutons         
    bouton_A(screen, (150, 250), "Activé")
    bouton_D(screen, (500, 250), "Désactivé")
    bouton_nb(screen, (265, 200), "1")
    bouton_nb(screen, (320, 200), "2")
    bouton_nb(screen, (375, 200), "3")
    bouton_nb(screen, (265, 255), "4")
    bouton_nb(screen, (320, 255), "5")
    bouton_nb(screen, (375, 255), "6")
    bouton_nb(screen, (265, 310), "7")
    bouton_nb(screen, (320, 310), "8")
    bouton_nb(screen, (375, 310), "9")
    
    
    
  
    

    img = font.render("Bienvenue", True, ROUGE)
    screen.blit(img, (20, 20))
    
    
    
    pygame.display.flip()
    
  
    
    e.image()
    e.horloge()
    
    FPS_CLOCK.tick(30)


pygame.quit()
sys.exit()

    
Reply
#2
The thread doesn't help to prevent blocking when you run the code that does the blocking outside of the thread,
Reply
#3
for example horloge() or the clock in english will stop working once the counter is starting.
What your saying even with a thread my clock will still freeze untill the 10 secondes is done.
The fact that there is a while loop in decompte() and reuse in the main while for pygame is probably my problem.
I tried to find a way on the web to create a countdown without while loop, a different way but nothing usefull.
So my teacher is wrong the thread wont solve the problem.
Reply
#4
No, what I am saying is that none of this code executes inside the thread.
    if led_off1==False and GPIO.input(senseur_Del2)==1:
                        thread = Thread(target=ent.delai, args=())   <-----maybe at the wrong place or stop at the wrong time
                        thread.start()
                        GPIO.output(DEL1,True)
                        time.sleep(.2)  # Not in a thread
                        GPIO.output(DEL1,False)
                        time.sleep(.2)  # Not in a thread
                        led_off2=True   # Should this be led_off1 == True?
                        sleep(.5)   # Not in  a thread
This may only be 0.9 seconds of delay, but it happens continuously as long as GPIO.input(senseur_Del1) == 1.

And you have a bunch of other waits that don't happen inside the thread.
Reply
#5
The way i understand it. The thread isolate the problematic function in this case delai() which is the counter. Thread isolate and run independantly the function. I did erase all the other dels to test if it was the problem. But even if i run only the function as a thread or not, the trouble is still there. led_off1 is just a light and senseur_Del2 is if the door is open or not. In this case if the door is open start the counter delai() you have 10 seconds to enter your code.
When this process is on the clock stop and it wont let activate the numbers on the keyboard. So its impossible to input the code.
I got to fix this by friday. That part doesnt worth many points. Still its a challenge, at least for me. Sad
Reply
#6
How many threads do you make? And before you say 1, maybe you should try a test.

These are all the same function other than rayon and color. You have arguments for pos and texte, why don't you do the same with rayon and color?
def bouton_A(screen, pos, texte):
    rayon = 55
    pygame.draw.circle(screen, GREEN, pos, rayon)
    txtBtn = font.render(texte, True, NOIR)
    rectBtn = txtBtn.get_rect()
    rectBtn.center = (pos)
    screen.blit(txtBtn, rectBtn)
     
def bouton_D(screen, pos, texte):
    rayon = 55
    pygame.draw.circle(screen, ROUGE, pos, rayon)
    txtBtn = font.render(texte, True, NOIR)
    rectBtn = txtBtn.get_rect()
    rectBtn.center = (pos)
    screen.blit(txtBtn, rectBtn)
     
def bouton_nb(screen, pos, texte):
    rayon = 25
    pygame.draw.circle(screen, GRIS, pos, rayon)
    txtBtn = font.render(texte, True, NOIR)
    rectBtn = txtBtn.get_rect()
    rectBtn.center = (pos)
    screen.blit(txtBtn, rectBtn)
Why did you make a class for this function? Why isn't delai a method of class Etat?
class Etat_ent:
    def delai(self): #délai d'entrée
            print('Vous avez 10 secondes avant l`enclenchement de l`alarme!')
            GPIO.output(DEL1,True)
            time.sleep(.1)
            decompte(int(t))
            GPIO.output(DEL1,True)
            time.sleep(1)
            GPIO.output(DEL1,False)
            time.sleep(1)
            GPIO.output(DEL1,True)
Etat really isn't a class either. What is it's purpose? It doesn't have any instance variables, so it's purpose cannot be to store information. None of the methods have anything in common. It is just a collection of functions that was put inside a class. Etat_ent could be a class. It could have instance variables and methods that work togeter to accomplish something like Countdwn in this example.
import pygame
import threading
import time
 
pygame.init()
FOREGROUND_COLOR = "White"
BACKGROUND_COLOR = "Black"
DEFAULT_FONT = pygame.font.SysFont(None, 32)
display = pygame.display.set_mode((300, 300))
screen = pygame.display.set_mode([450, 350])
 
class Countdown:
    """A countdown timer that calls a function when it counts down to zero"""
    def __init__(self):
        self.callback = None
        self.counting = False

    def doit(self, start_count):
        """Does the countdown.  Runs in its own thread so it doesn't block."""
        self.counting = True
        for count in range(start_count, 0, -1):
            if not self.counting:
                break
            label.set_text(str(count))
            time.sleep(1)
        else:
            if self.callback is not None:
                self.callback()          

    def connect(self, func):
        """Specify the function called when the timer counts down to zero"""
        self.callback = func

    def start(self, start_count):
        """Start the countdown"""
        if not self.counting:
            threading.Thread(target=self.doit, args=(start_count,)).start()

    def stop(self):
        """Stop the countdown"""
        self.counting = False
        label.set_text("")


class Widget(pygame.surface.Surface):
    """Base class for pygame widgets"""
    def __init__(self, parent, size, foreground=None):
        super().__init__(size)
        self.parent = parent
        self.foreground = FOREGROUND_COLOR if foreground is None else foreground
        self.background = BACKGROUND_COLOR
        self.rect = self.get_rect()
 
    def draw(self):
        """Draw (blit) self on parent surface."""
        self.parent.blit(self, (self.rect.x, self.rect.y))
        return self
 
    def at(self, x, y):
        """Set upper left corner at x, y"""
        self.rect.x = x
        self.rect.y = y
        return self
 
    def center_at(self, x, y):
        """Set center at x, y"""
        self.rect.centerx = x
        self.rect.centery = y
        return self
 
 
class Label(Widget):
    """A tkinter Label like thing for pygame"""
    def __init__(self, parent, text, width=None, font=None, foreground=None):
        self.width = len(text) if width is None else width
        self.font = DEFAULT_FONT if font is None else font
        size = self.font.render("W"*self.width, True, BACKGROUND_COLOR).get_size()
        super().__init__(parent, size, foreground)
        self.set_text(text)
 
    def set_text(self, text):
        self.text = text
        text_img = self.font.render(self.text, True, self.foreground)
        y = (self.rect.height - text_img.get_height()) // 2
        self.fill(self.background)
        self.blit(text_img, (0, y))
        self.draw()
        return self
 
 
class CircleButton(Widget):
    """A tkinter Button like think for pygame"""
    buttons = []
 
    @classmethod
    def clicked(cls, x, y):
        """Call this method to find what button was pressed"""
        for button in cls.buttons:
            if button.click(x, y):
                return True
        return False
 
    def __init__(self, parent, text, radius, foreground=None, text_color=None, font=None):
        super().__init__(parent, (radius*2, radius*2), foreground)
        self.font = DEFAULT_FONT if font is None else font
        self.text_color = self.background if text_color is None else text_color
        self.callback = None
        self.buttons.append(self)
        self.set_text(text)
 
    def set_text(self, text):
        """Set my label text.  Label is centered in button"""
        self.text = text
        text_img = self.font.render(self.text, True, self.text_color)
        x = (self.rect.width - text_img.get_width()) // 2
        y = (self.rect.height - text_img.get_height()) // 2
        self.fill(self.background)
        pygame.draw.ellipse(self, self.foreground, self.get_rect())
        self.blit(text_img, (x, y))
        return self
 
    def connect(self, func):
        """Set function to call when clicked"""
        self.callback = func
        return self
 
    def click(self, x, y):
        """Check if I was clicked.  Execute callback method if clicked"""
        clicked = self.rect.collidepoint(x, y)
        if clicked and self.callback:
            self.callback(self)
        return clicked

# Give the buttons something to do
def button_pressed(button):
    label.set_text(label.text + button.text)
 
label = Label(screen, "", width=20, font=pygame.font.SysFont(None, 36)).at(50, 50)
 
# Create a countdown timer that goes BOOM!! when it counts all the way down.
countdown = Countdown()
countdown.connect(lambda: label.set_text("BOOM!!"))

# Make some buttons
for number in range(9):
    x = 50 + (number % 3) * 80
    y = 260 - (number // 3) * 80
    CircleButton(screen, str(number+1), 30, "Blue", "White") \
        .connect(button_pressed).at(x, y).draw()

CircleButton(screen, "Start", 55, "Green") \
    .connect(lambda x: countdown.start(10)).at(300, 100).draw()

CircleButton(screen, "Stop", 55, "Red") \
    .connect(lambda x: countdown.stop()).at(300, 220).draw()

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            break
 
        if event.type == pygame.MOUSEBUTTONUP:
            CircleButton.clicked(*pygame.mouse.get_pos())
 
    pygame.display.flip()
    pygame.display.update()
 
pygame.quit()
Reply
#7
Added in a clock display that makes it easier to see that the thread runs the countdown without blocking the pytgame event loop.
import pygame
import threading
import time

pygame.init()
FOREGROUND_COLOR = "White"
BACKGROUND_COLOR = "Black"
DEFAULT_FONT = pygame.font.SysFont(None, 36)
CLOCK_FONT = pygame.font.SysFont(None, 20)
screen = pygame.display.set_mode([390, 300])


class Countdown:
    """A countdown timer that calls a function when it counts down to zero"""
    def __init__(self, display):
        self.display = display
        self.callback = None
        self.counting = False

    def doit(self, start_count):
        """Does the countdown.  Runs in its own thread so it doesn't block."""
        self.counting = True
        for count in range(start_count, 0, -1):
            if not self.counting:
                break
            self.display.set_text(f"Countdown: {count}")
            time.sleep(1)
        else:
            self.counting = False
            if self.callback is not None:
                self.callback()

    def connect(self, func):
        """Specify the function called when the timer counts down to zero"""
        self.callback = func

    def start(self, start_count):
        """Start the countdown"""
        if not self.counting:
            threading.Thread(target=self.doit, args=(start_count,)).start()

    def stop(self):
        """Stop the countdown"""
        self.counting = False
        self.display.set_text("")


class Widget(pygame.surface.Surface):
    """Base class for pygame widgets"""
    def __init__(self, parent, size, foreground=None, background=None):
        super().__init__(size)
        self.parent = parent
        self.foreground = FOREGROUND_COLOR if foreground is None else foreground
        self.background = BACKGROUND_COLOR if background is None else background
        self.rect = self.get_rect()

    def draw(self):
        """Draw (blit) self on parent surface."""
        self.parent.blit(self, (self.rect.x, self.rect.y))
        return self

    def erase(self):
        """Draw (blit) background color on parent surface"""
        pygame.draw.rect(self.parent, BACKGROUND_COLOR, self.rect)
        return self

    def at(self, x, y):
        """Set upper left corner at x, y"""
        self.rect.x = x
        self.rect.y = y
        return self

    def center_at(self, x, y):
        """Set center at x, y"""
        self.rect.centerx = x
        self.rect.centery = y
        return self

    @property
    def x(self):
        return self.rect.x

    @x.setter
    def x(self, new_x):
        self.rect.x = new_x

    @property
    def y(self):
        return self.rect.y

    @y.setter
    def y(self, new_y):
        self.rect.y = new_y

    @property
    def width(self):
        return self.rect.width

    @width.setter
    def width(self, new_width):
        self.rect.width = new_width

    @property
    def height(self):
        return self.rect.height

    @height.setter
    def height(self, new_height):
        self.rect.height = new_height


class Label(Widget):
    """A tkinter Label like thing for pygame"""
    def __init__(self, parent, text, width=None, font=None, foreground=None, background=None):
        width = width or len(text)
        self.font = DEFAULT_FONT if font is None else font
        size = self.font.render("W"*width, True, BACKGROUND_COLOR).get_size()
        super().__init__(parent, size, foreground, background)
        self.set_text(text)

    def set_text(self, text):
        self.text = text
        text_img = self.font.render(self.text, True, self.foreground)
        y = (self.rect.height - text_img.get_height()) // 2
        self.fill(self.background)
        self.blit(text_img, (0, y))
        self.draw()
        return self


class CircleButton(Widget):
    """A tkinter Button like think for pygame"""
    buttons = []

    @classmethod
    def clicked(cls, x, y):
        """Call this method to find what button was pressed"""
        for button in cls.buttons:
            if button.click(x, y):
                return True
        return False

    def __init__(self, parent, text, radius, foreground=None, text_color=None, font=None):
        super().__init__(parent, (radius*2, radius*2), foreground)
        self.font = DEFAULT_FONT if font is None else font
        self.text_color = self.background if text_color is None else text_color
        self.callback = None
        self.buttons.append(self)
        self.set_text(text)

    def set_text(self, text):
        """Set my label text.  Label is centered in button"""
        self.text = text
        text_img = self.font.render(self.text, True, self.text_color)
        x = (self.rect.width - text_img.get_width()) // 2
        y = (self.rect.height - text_img.get_height()) // 2
        self.fill(self.background)
        pygame.draw.ellipse(self, self.foreground, self.get_rect())
        self.blit(text_img, (x, y))
        return self

    def connect(self, func):
        """Set function to call when clicked"""
        self.callback = func
        return self

    def click(self, x, y):
        """Check if I was clicked.  Execute callback method if clicked"""
        clicked = self.rect.collidepoint(x, y)
        if clicked and self.callback:
            self.callback(self)
        return clicked

# Give the buttons something to do
def button_pressed(button):
    display.set_text(display.text + button.text)

clock_display = Label(screen, "", width=8, font=CLOCK_FONT).at(300, 20)
display = Label(screen, "", width=10, foreground="black", background="white")
display.erase().at(20, 20).draw()

# Create a countdown timer that goes BOOM!! when it counts all the way down.
countdown = Countdown(display)
countdown.connect(lambda: display.set_text("BOOM!!"))

# Make some buttons
for number in range(9):
    x = 20 + (number % 3) * 80
    y = 220 - (number // 3) * 80
    CircleButton(screen, str(number+1), 30, "Blue", "White") \
        .connect(button_pressed).at(x, y).draw()

CircleButton(screen, "Start", 50, "Green") \
    .connect(lambda x: countdown.start(10)).at(270, 60).draw()

CircleButton(screen, "Stop", 50, "Red") \
    .connect(lambda x: countdown.stop()).at(270, 180).draw()

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            break

        if event.type == pygame.MOUSEBUTTONUP:
            CircleButton.clicked(*pygame.mouse.get_pos())

    clock_display.set_text(time.strftime("%H:%M:%S", time.localtime()))
    pygame.display.flip()
    pygame.display.update()

pygame.quit()
Frankduc likes this post
Reply
#8
Quote:Why did you make a class for this function? Why isn't delai a method of class Etat?

These are all good questions. Quick answer i still dont grasp the concept of classes and state machine. I gave it a try because the teacher ask us to input those concepts in the assigment. delai returned an error message that i could not fix.

Anyway i am about to quit i dont really like programming.
Thank you for your help and time.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Error SQLite objects created in a thread can only be used in that same thread. binhduonggttn 3 15,601 Jan-31-2020, 11:08 AM
Last Post: DeaD_EyE

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020