Pause the code at some point - python

I want to pause the code at exact position and wait for the different input plus the start button clicked. However, if it is not achievable, how can I add another button into the start button to make it work?
import wx
import time
import RPi.GPIO as GPIO
global Total_Length
Total_Length = 500
global Input_Length
Input_Length = 0
a = 0
class tyler(wx.Frame):
def __init__(self,parent,id):
wx.Frame.__init__(self,parent,id,'Lyquid Crystal Laser Control',size=(500,200))
panel=wx.Panel(self)
#global Text_1
self.Text_1 = wx.TextCtrl(panel,-1,"0",(350,30),(50,30))
self.Text_1.Bind(wx.EVT_TEXT_ENTER,self.Start)
self.Text_1.Bind(wx.EVT_KILL_FOCUS,self.Start)
self.timer = wx.Timer(self)
#self.Bind(wx.EVT_TIMER, self.timer)
button_1=wx.Button(panel,label="Start",pos=(400,80),size=(80,30))
button_2=wx.Button(panel,label="Stop",pos=(400,120),size=(80,30))
self.Bind(wx.EVT_BUTTON, self.Start, button_1)
#self.Bind(wx.EVT_BUTTON, self.Stop, button_2)
def Start(self,event):
global a
Input_Length=float(self.Text_1.GetValue())
#print(Input_Length)
#a = Input_Length
#print(Input_Length)
dc=float(100*Input_Length/Total_Length)
GPIO.setmode(GPIO.BCM)
GPIO.setup(18,GPIO.OUT)
GPIO.setwarnings(False)
p = GPIO.PWM(18,1150)
p.start(0)
p.ChangeDutyCycle(dc)
p.ChangeFrequency(1150)
#I wanted to pause the code at here, until the input changes, and the start button clicked, so I add timer in below, however, the output is only a pulse but the square wave is what I wanted
if a == dc:
self.timer.Start(1000)
else:
a = dc
self.timer.Stop()
#def Stop(self,event):
GPIO.cleanup()
if __name__=='__main__':
app=wx.PySimpleApp()
frame=tyler(parent=None,id=-1)
frame.Show()
app.MainLoop()

"Pause and wait" and "event-driven GUI programming" do not go together. As long as the main GUI thread is blocked waiting for something, then other events can't be processed and the program will appear to be frozen. Your options are to change how you "wait" (by not actually waiting) or to use another thread.
This answer to another question applies just as well here, and will give you more information and pointers.

Related

QPushButton / GPIO on-delay

I am trying to create a GUI for touchscreen light controller for RPi using python. So far I have three QPushButtons on my GUI; one for on/off (single click), one for dimming (long press) and one for led mode change (single click). Each of these control corresponding GPIO to control relay.
For the dimming, my LED requires long signal. If this signal is too short, LED driver considers this as a click and it will work as a on/off switch, which I don't want to happen. For this I need to create some type of script that checks that the QPushButton (name: btn_dim) is pressed longer than x amounts of seconds (let's say 0.5 sec) and then perform desired function (turn GPIO on). Icon on the button should also change when the button is pressed even if the press is shorter than 0.5 sec.
Here's what I got so far:
class MyWindow(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.btn_close.clicked.connect(self.appClose)
self.btn_dim.pressed.connect(self.dimOn)
self.btn_dim.released.connect(self.dimOff)
self.iconDIM_off = QtGui.QIcon()
self.iconDIM_off.addPixmap(QtGui.QPixmap("icons/led/LED-DIM-OFF.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.iconDIM_on = QtGui.QIcon()
self.iconDIM_on.addPixmap(QtGui.QPixmap("icons/led/LED-DIM-ON.png"))
# App close
def appClose(self):
GPIO.cleanup()
sys.exit()
def dimOn(self):
self.btn_dim.setIcon(self.iconDIM_on)
t_start = time.perf_counter()
print(t_start)
time.sleep(0.5)
if self.btn_dim.isChecked:
t_end = time.perf_counter()
print(t_end)
t_press = (t_end - t_start)
print(t_press)
if t_press > 0.5:
GPIO.output(5, 0)
def dimOff(self):
self.btn_dim.setIcon(self.iconDIM_off)
GPIO.output(5, 1)
Now what happens is:
Icon changes after 0.5 sec, not immediately
Even with a click, GPIO turns on after 0.5 sec and immediately off
How do I modify this, so that the icon changes even with a click/short press, but the GPIO turns on only if the press is longer than 0.5 sec.
Got it work with this:
# Dimmer delay to differentiate from click
def dimmerTimer(self):
time.sleep(0.25)
start=self.pressed
end=self.released
press_time=(end-start)
if press_time < 0:
GPIO.output(13, 0)
else:
GPIO.output(13, 1)
# Dimmer pushbutton pressed function
def dimPressed(self):
self.btn_dim.setIcon(self.iconDIM_on)
self.pressed = time.perf_counter()
timer = Thread(target=self.dimmerTimer)
timer.start()
# Dimmer pushbutton released function
def dimReleased(self):
self.btn_dim.setIcon(self.iconDIM_off)
self.released = time.perf_counter()
time.sleep(0.35)
GPIO.output(13, 1)
There's probably better or right way to do this, but this did the trick for me. Hopefully it's helpful to someone!

I want to toggle a real pushbutton and display it on tkinter GUI

I want to toggle a pushbutton and show its changes on a label using tkinter.
If I press the button it shows "on" on the label and when I press again it shows "off" on the label
So I try these codes and If I'm trying the wrong code please help me write the correct using tkinter.
I have a problem in combining this code
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(22,GPIO.IN,up_down=GPIO.PUD_UP)
while(1):
if GPIO.input(22)==1:
if bs == False :
x.set("on")
bs=True
sleep(0.5)
else:
x.set("off")
bs=False
sleep(0.5)
This works okay but I want to connect it to a GUI label to print on it on or off.
Here is the tkinter code
import tkinter.*
root = tk()
x = StringVar()
s=Label(root,textvariable=x)
s.grid(column=0,row=0)
root.mainloop()
When I try to combine it I make it like this
from Tkinter import *
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
GPIO.setup(7,GPIO.IN)
b=False
def check_button1():
if GPIO.input(7)== 1:
if b == False :
labelText1.set("on")
print"on"
b=True
time.sleep(0.5)
else:
labelText1.set("off")
print"off"
b=False
time.sleep(0.5)
mamdouh.after(10,check_button1)
mamdouh = Tk()
labelText1 = StringVar()
x1 = Label(mamdouh,textvariable=labelText1)
x1.config(font=('Helvetica',25,'bold'))
x1.grid(row=0,column=0)
mamdouh.title("mamdouh")
mamdouh.geometry('1200x700')
mamdouh.after(10,check_button1)
mamdouh.mainloop()
but it didn't works it keeps blank every time I press the push button actually If it works well I will put 17 push button
I think that the problem is in placing this if statment on the right place and placing the b variable in it's right place and I think also there is a problem between this if statment and tkinter because I tried this code wich works perfect but it is not toggling the push button so I want to change this lets add this code here also :
from Tkinter import *
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setup(7,GPIO.IN)
def check_button1():
if(GPIO.input(7) == GPIO.LOW):
labelText1.set("on")
else:
labelText1.set("off")
mamdouh.after(10,check_button1)
mamdouh = Tk()
labelText1 = StringVar()
x1 = Label(mamdouh,textvariable=labelText1)
x1.config(font=('Helvetica',25,'bold'))
x1.grid(row=0,column=0)
mamdouh.title("mamdouh")
mamdouh.geometry('1200x700')
mamdouh.after(10,check_button1)
mamdouh.mainloop()
So how I can make this toggle push button on an Label?
Your problem is recognizing button down and button up events. Your OS mouse driver does this for your mouse buttons. If your GPIO module does not do this for you, you will have to detect these events by comparing the current state to the previous state. (I am ignoring here any possible need to 'de-bounce' the button.) You are sort of trying to do this with the time.sleep(.5) calls, but do not use time.sleep in gui code.
Your driver should be self-contained and independent of any tk widgets other than the root needed for .after. For multiple buttons, you will need your own GPIOButton class. Your code that works is a starting point. Tkinter allows you to tie a command to button-up events. Your class init should similarly take up and or down event commands (callbacks).
Here is something untested that might get you started.
class GPIOButton:
def __init__(self, master, buttons, command_down=None, command_up=None):
self.master = master
self.buttons = buttons
self.command_down = command_down
self.command_up = command_up
GPIO.setmode(GPIO.BOARD)
for button in buttons:
GPIO.setup(button, GPIO.IN)
self.state = [GPIO.HIGH] * len(buttons) # best initial value?
self.check_buttons() # or call this elsewhere
def check_buttons(self):
for i, button in enumerate(self.buttons):
oldstate = self.state[i]
newstate = GPIO.input(button)
if oldstate != newstate:
self.state[i] = newstate
command = (self.command_down if newstate==GPIO.LOW
else self.command_up)
command(button)
self.master.after(10, self.check_button)
Let me preface my answer with a disclaimer—I don't have a Raspberry Pi, so couldn't verify this works with the real thing. For testing I used a proxy class that simulates random button pressing. You may have to adjust the DELAY value depending on how fast the GPIO interface works.
However, I have put commented-out code in near the top showing what I think you would need to use based on of doing so in your code.
try:
import Tkinter as tk
import tkFont
except ImportError: # Python 3
import tkinter as tk
import tkinter.font as tkFont
#import RPi.GPIO as GPIO
#
#GPIO.setmode(GPIO.BOARD)
#
#class GPIOButton(object):
# """ Encapsulates GPIO button interface. """
# def __init__(self, pin):
# self.pin = pin
# self.status = 0
# GPIO.setup(pin, GPIO.IN)
#
# def update_status(self):
# self.status = GPIO.input(pin) == GPIO.LOW
### Proxy class since I don't have a Rasperry Pi. ###
import random
class GPIOButton(object):
def __init__(self, pin):
self.pin = pin
self.status = 0
def update_status(self):
if not random.randint(0, 99) % 20: # occassionally toggle status
self.status = not self.status
class App(tk.Frame):
STRIDE = 8
DELAY = 100 # delay in millsecs between button status updates
def __init__(self, gpio_buttons, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.gpio_buttons = gpio_buttons
self.create_widgets()
self.after(self.DELAY, self.update_buttons, self.DELAY) # start updates
def create_widgets(self):
self.btn_font = tkFont.Font(family="Helvetica", size=12, weight='bold')
self.gui_buttons = []
for i, button in enumerate(self.gpio_buttons):
is_pressed = tk.BooleanVar()
is_pressed.set(False)
radiobutton = tk.Radiobutton(self,
text=format(i+1, '02d'),
font=self.btn_font,
value=True,
variable=is_pressed,
relief=tk.RIDGE)
row, col = divmod(i, self.STRIDE)
radiobutton.grid(column=col, row=row)
self.gui_buttons.append(is_pressed)
def update_buttons(self, delay):
for i, gpio_button in enumerate(self.gpio_buttons):
previous_status = gpio_button.status
gpio_button.update_status()
if gpio_button.status != previous_status:
self.gui_buttons[i].set(gpio_button.status)
self.after(delay, self.update_buttons, delay) # rinse and repeat
gpio_buttons = [GPIOButton(pin) for pin in range(16)]
app = App(gpio_buttons)
app.master.title('Rasberry Pi Buttons')
app.mainloop()
Here's what the simulation looks like running on my Windows computer:

Python win32api.SetCursorPos dual screen issue

I am trying to create simple program which will click to specific coordinates every x seconds based on your choose if you need to click on Left screen or Right screen. My issue here is that win32api.SetCursorPos which is moving with my cursor is not moving to the secondary screen (right in my case). It stays in the main screen.
And I am having one more issue with my code, when Exit button inside GUI is pressed, window will close however program is still running in background. I am using self.Destroy() function to kill all process.
Thank you for your advice.
Here is my code:
import time
import pyautogui
import wx
import threading
import sys
import win32api
class bucky(wx.Frame):
def __init__(self,parent,id):
self.positionx = ""
self.positiony = ""
wx.Frame.__init__(self,parent,id,'AutoClick 2.0', size=(300,200))
panel=wx.Panel(self)
self.buttonpos=wx.Button(panel,label="Left Screen",pos=(30,10),size=(80,40))
self.buttonpos2=wx.Button(panel,label="Right Screen",pos=(180,10),size=(80,40))
self.button=wx.Button(panel,label="Start",pos=(120,90),size=(60,30))
self.button2=wx.Button(panel,wx.ID_EXIT,label="Exit",pos=(120,120),size=(60,30))
self.Bind(wx.EVT_BUTTON, self.action, self.button)
self.Bind(wx.EVT_BUTTON, self.closebutton, self.button2)
self.Bind(wx.EVT_BUTTON, self.position, self.buttonpos)
self.Bind(wx.EVT_BUTTON, self.position, self.buttonpos2)
self.Bind(wx.EVT_CLOSE, self.closewindow)
def position(self, event):
label = event.GetEventObject().GetLabel()
if label == "Left Screen":
self.positionx = 1640
self.positiony = 183
self.buttonpos.Disable()
self.buttonpos2.Enable()
elif label == "Right Screen":
self.positionx = 3308
self.positiony= 186
self.buttonpos.Enable()
self.buttonpos2.Disable()
def closebutton(self,event):
self.Destroy()
def closewindow(self,event):
self.Destroy()
def action(self,event):
self.button.Disable()
def callback():
while 1:
pos = pyautogui.position()
time.sleep(10)
pos1 = pyautogui.position()
if (pos1[0] == pos[0]) and (pos1[1] == pos[1]):
win32api.SetCursorPos((self.positionx, self.positiony))
pyautogui.click()
else:
pass
t = threading.Thread(target=callback)
t.start()
if __name__=='__main__':
app=wx.PySimpleApp()
frame=bucky(parent=None,id=1)
frame.Show()
app.MainLoop()
EDIT: Problem have been solved. Thank you for help.
Looking at this reddit post, I think the x coordinates for the secondary monitor are simply added to the resolution of the primary monitor (ex if you had two 1920x1080 monitors, the middle of the second monitor will be at 2880,520)
Try using (win32api.GetSystemMetrics(MONITOR_NUMBER) to see how the differences are represented.

Binding a Pyo Oscillator output to a WX Event

I am building a simple signal generator in Python based on the Pyo and WX libraries.
I have ran through the simple tutorials for each and have successfully bound buttons in WX to WX functions. I am now trying to generate a simple sine wave(at 440 hz) for 1 second by pressing the button labeled "Oscillator 1"; however, when the main() function executes, the sine tone is played and while the button is displayed in the wx frame I am unable to retrigger the sine tone. Both of these symptoms are unwanted.
Why does the sine tone play immediately on program execution? Why does the firstOSC button seemingly not work?
import wx
from pyo import *
import time
pyoServer = Server().boot()
pyoServer.start()
class MainWindow(wx.Frame):
def __init__(self,parent,title):
wx.Frame.__init__(self,parent,title=title, size = (640,640))
self.CreateStatusBar() # A StatusBar in the bottom of the window
# Signal Generator controls
oscillator = SoundOutput()
firstOSC = wx.Button(self, wx.ID_YES,"Oscillator 1 " + str(oscillator.str_osc1State))
self.Bind(wx.EVT_BUTTON, oscillator.OnOff1(440), firstOSC)
#Menus
filemenu = wx.Menu()
menuExit = filemenu.Append(wx.ID_EXIT,"&Exit","Terminate the program")
menuBar = wx.MenuBar()
menuBar.Append(filemenu,"&File")
self.SetMenuBar(menuBar)
self.Bind(wx.EVT_MENU, self.OnExit, menuExit)
self.Show(True)
def OnExit(self,e):
self.Close(True)
class SoundOutput(object):
def __init__(self):
self.osc1State = False
self.str_osc1State = "Off"
self.a = Sine(440, 0, 0.1)
def OnOff1(self, frequency):
self.a.freq = frequency
self.a.out()
time.sleep(1)
self.osc1State = True
def Main():
app = wx.App(False)
frame = MainWindow(None,"Signal Generator")
app.MainLoop()
I solved this by investigating how WX handles events. As it turns out, for some reason calling a method in a nested or separate instance of a class caused the tone to play at runtime instead of on the event. I fixed this by making a method for the MainWindow class that serves as the binded event handler for firstOSC. This method then calls the requisite methods for the actual oscillator class.
Here is the new code:
# Signal Generator controls
self.fOscillator = SoundOutput()
self.fOscillatorstatus = False
self.firstOSC = wx.Button(self, wx.ID_ANY,"Oscillator 1 On")
self.firstOSC.Bind(wx.EVT_BUTTON, self.OnFirstOSC)
def OnFirstOSC(self,e):
if not self.fOscillatorstatus:
self.fOscillator.OnOff1(440)
self.fOscillatorstatus = True
self.firstOSC.SetLabel("Oscillator 1 Off")
elif self.fOscillatorstatus:
self.fOscillator.OnOff1(0)
self.firstOSC.SetLabel("Oscillator 1 On")
self.fOscillatorstatus = False

Get rid of a label inside a thread?

so i have this code:
import thread
from Tkinter import *
import random
import time
Admin=Tk()
def moveit(number):
songas=Label(Admin,text=number,bg='red')
def ji():
plad=0.0
recount=0
times=0
while 1:
plad-=0.1
recount+=1
times+=1
time.sleep(0.5)
pls=0.0
pls+=plad
if recount==4:
pls=0
plad=0.0
recount=0
songas.place(relx=pls,rely=0.7)
thread.start_new_thread(ji,())
za=random.random()
button=Button(Admin,text='Press',command=lambda:moveit(str(za)))
button.place(relx=0.2)
Admin.mainloop()
And it starts to move to the left but if you press the 'press' button again it puts some more numbers on top of the old ones.
does any one know how to erase the old numbers to make it so there are only the knew ones?
Tkinter isn't thread safe -- you can't manipulate widgets in any thread except the main one or you'll get undefined results.
You don't need threads for this. Your code adds an infinite loop, but the application already has an infinite loop (the event loop) that you can take advantage of.
If you want to move some item create a function that does two things. First, it does whatever it is you want, such as move the item. Second, it uses the standard after method to call itself again in a short amount of time (for example, half a second or 500ms). This way you let your event loop drive the animation, you don't need threads, and your UI stays responsive.
Here's an example. I doubt it does exactly what you want because I'm not certain of exactly what you want.
import Tkinter as tk
import random
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
self._running = False
self._relx = None
tk.Tk.__init__(self, *args, **kwargs)
self.pack_propagate(False)
self.configure(width=400, height=400)
self.label = tk.Label(self, text="hello, world", background="red")
self.button = tk.Button(self, text="Start", command=self.toggle)
self.button.pack(side="top")
def toggle(self):
'''toggle animation on or off'''
self._running = not self._running
if self._running:
self.button.configure(text="Stop")
self.moveit()
else:
self.button.configure(text="Start")
def moveit(self):
'''Animate the label'''
if not self._running:
# animation has been stopped
# hide the label from view.
self.label.place_forget()
if self._running:
if not self.label.winfo_viewable():
# not visible; establish future locations
self._relx = [.5, .4, .3, .2, .1, 0]
relx = self._relx.pop(0)
self._relx.append(relx)
self.label.place(relx=relx, rely=0.7)
self.after(1000, self.moveit)
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
You must signal the old thread to exit somehow. It is probably easiest to perform with locks - you create a lock when creating a new thread and acquire it. And you release it when the thread is no longer needed. The thread then only needs to check in the main loop whether its lock is still locked - if it isn't it will remove the label and exit. Here the modified version of your code (replace "Remove label here" comment by suitable code):
import thread
from Tkinter import *
import random
import time
Admin=Tk()
lock = None
def moveit(number):
global lock
songas=Label(Admin,text=number,bg='red')
def ji(lock):
plad=0.0
recount=0
times=0
while 1:
plad-=0.1
recount+=1
times+=1
time.sleep(0.5)
pls=0.0
pls+=plad
if recount==4:
pls=0
plad=0.0
recount=0
songas.place(relx=pls,rely=0.7)
if not lock.locked():
# Remove label here
break
if lock:
# Signal old thread to exit
lock.release()
lock = thread.allocate_lock()
lock.acquire()
thread.start_new_thread(ji,(lock,))
za=random.random()
button=Button(Admin,text='Press',command=lambda:moveit(str(za)))
button.place(relx=0.2)
Admin.mainloop()

Categories