I'm trying to create a small GUI application where both the user and the computer are clicking the button widget turn by turn.
First, the user Click the Button and the Button text changes, then the computer clicks the button and the different texts appear on the button for the machine
Here's the code.
from tkinter import *
globals()['status'] = True
class test11(Tk):
def __init__(self):
super().__init__()
self.geometry('200x200')
self.btn_1 = Button(self,text="CLICK ME",command=lambda :self.test_function(1,2))
self.btn_1.grid()
if not (globals()['status']):
self.machine()
def test_function(self,aa,bb):
self.btn_1['text'] = f'Dont CLICK ME {aa} {bb} HUMAN'
globals()['status'] = False
def machine(self):
self.btn_1['text'] = f'Computer just clicked me'
globals()['status'] = True
if __name__ == '__main__':
a = test11()
a.mainloop()
But the problem is the turn of the machine never comes even though the user has already pressed the button.
As the user presses the button the values of global variable changes which should have let the machine function to call isn't it?
Related
i have a folderscreen that displays buttons from a loop, the buttons have title of the files saved, when i click the buttons, it opens a new page that shows the content of the file, but when i go back to the folder screen, 2 sets of the button in the loop are added to the existing buttons.
what i want is everytime i leave the folderscreen, i want the buttons cleared, so that when i go back, it will run the loop code again and shows only the buttons from the loop without repetition.
or may be there is a way i can stop the loop code from running if the boxlayout that house the buttons is not empty. here is my code:
def on_enter(self, *args):
objf = mm.Storenote()
objf.tif(mm.DB.hod)
for i in range(len(mm.Storenote.ht)):
self.b = Button(text= (mm.Storenote.ht[i]), font_size = "25sp")
#self.b.background_normal = ""
self.b.background_color = 0,0,1,1
self.b.ids = {"id":mm.Storenote.nid[i]}
self.b.size_hint=(1, None)
self.b.size = (dp(370), dp(50))
self.b.bind(on_press=self.build_clickk(self.b))
self.ids.lpl.add_widget(self.b)
#self.ids.lpl.add_widget(self.l[i])
def build_clickk(self, b):
def clickk(*args):
ci = b.ids.id
print(ci)
objff = mm.Show()
objff.getter(ci)
self.manager.current = "Readpage"
return clickk
def on_leave(self, *args):
self.ids.lpl.remove_widget(self.b)
the def on_leave function only remove one of the buttons and add 2 new sets of the button each time i go back to the folderscreen
You can use self.ids.lpl.clear_widgets() right before your for-loop. This way you ensure that the layout is empty before adding new widgets.
If you add many widgets, try using def on_pre_enter(). kivy will render the widgets before entering the screen and this may prevent "flickering" when building all widgets.
Here is my code
app = QApplication(sys.argv)
loginWindow = loadUi('~/mainwindow.ui')
okconnect = loadUi('~/okconnect.ui')
def prueba2(test):
print(test + " Testing2")
def prueba(test):
print (test)
okconnect.show()
okconnect.getvmsButton.clicked.connect(lambda: prueba2(test))
loginWindow.show()
loginWindow.connectButton.clicked.connect(lambda: prueba("test"))
sys.exit(app.exec_())
When I press "connect" button, a new window is opened, with a button, "getvmsButton", when I press this button, its show in console the print line in def prueba2(test), but if I close the window, and then I click again in connect button, the window open again and "getvmsButton" is pressed again, the console show 2 messages instead of 1. If I repeat the process, more messages are showed.
What should I change to show only one messages when I close and open several times the window?
When you close your GUI by 'x' doesn't mean you disconnected the button click signal, therefore, every time you click the 'connect' button, the loginWindow will create a new button.clicked event and keep the old ones. A quick solution to this is to define a close-event for the 'x' clicks. Here provided is just a way to close the old signal events.
from PyQt5.QtWidgets import QWidget
OKConnect(QWidget):
def __init__(self):
super().__init__()
self.okconnect = loadUi('~/okconnect.ui')
self.okconnect.closeEvent = self.closeEvent
def closeEvent(self, event):
print("testing2 closing")
# to close the old getvmsButton signals
self.okconnect.getvmsButton.disconnect()
# Here below is exactly your code
app = QApplication(sys.argv)
loginWindow = loadUi('~/mainwindow.ui')
okconnect = OKConnect()
def prueba2(test):
print(test + " Testing2")
def prueba(test):
print (test)
okconnect.show()
okconnect.getvmsButton.clicked.connect(lambda: prueba2(test))
loginWindow.show()
loginWindow.connectButton.clicked.connect(lambda: prueba("test"))
sys.exit(app.exec_())
I am not sure about your .ui files, the code may not work on yours.
I researched this topic and could not find exactly what I needed to get the results I needed. I am fairly new to Python and new to Tkinter, so any help would be greatly appreciated.
I am using Python code through the Raspberry Pi to control a Arduino Uno, which will ultimately control servos to control a remote control car.
I am using the GUI through Raspberry Pi and using Tkinter commands such as button to interact through the GUI. The buttons are four, which are 'Up', 'Down' ,'left' and 'right'.
Right now I am using the Serial Monitor through the Sketch Arduino program to debug the code.
Although the code is working and is sending a signal to the Arduino, it is not working as I would have hoped. Right now it is initiating the command after you let go of the button and only for a moment.
What I need: For the signal to be sent to the Arduino the moment the button is pressed, and continue to send the signal as long as the button is being pressed and cut off as soon as the button is released.
Here is the code:
from Tkinter import *
import serial
running = True
ser = serial.Serial('/dev/ttyUSB0')
class Application(Frame):
"""Defining the remote control buttons"""
def __init__(self,master):
"""Initialize the frame"""
Frame.__init__(self,master)
self.grid() # How many times has the user clicked the button
self.create_widgets()
def create_widgets(self):
"""Creates four buttons that move the servos"""
#Create the 'up' button
self.bttn1 = ButtonPress(self)
self.bttn1["text"] = "Up"
self.bttn1["command"] = self.up
self.bttn1.grid()
#Create the 'down' button
self.bttn2 = Button(self)
self.bttn2.grid()
self.bttn2["text"] = "Down"
self.bttn2["command"] = self.down
#Create the 'left' button
self.bttn3 = Button(self)
self.bttn3.grid()
self.bttn3["text"] = "Left"
self.bttn3["command"] = self.left
#create the 'right' button
self.bttn4 = Button(self)
self.bttn4.grid()
self.bttn4["text"] = "Right"
self.bttn4["command"] = self.right
#Tells python to send the data to Arduino via serial
def right(self):
ser.write('3')
def left(self):
ser.write('4')
def up(self):
ser.write('1')
def down(self):
ser.write('2')
#Main
root = Tk()
root.title("Remote control")
root.geometry("250x250")
app = Application(root)
root.mainloop()
Buttons in tkinter by default don't fire until you release the mouse button. However, you can set bindings on both a button press and a button release. You can make bindings that set a flag on press and unset it on release, and then create a function that sends data while the flag is set.
There are several ways to implement this. One way is to have a function that runs periodically, and sends the key if the button is pressed and doesn't if it's unset. Another way is to only have it run while the key is pressed. For simplicity's sake, I'll show the first method.
In this example I'll use the flag not only to determine if data should be sent, but also to define what data is to be sent. That is by no means a requirement, it just cuts down a little on how much code you have to write.
def send_data(self):
if self.char is not None:
ser.write(self.char)
# run again in 100ms. Here is where you control how fast
# to send data. The first parameter to after is a number
# of milliseconds to wait before calling the function
self.job = self.after(100, self.send_data)
The above code will run approximately every 100 milliseconds, sending whatever character is set (or nothing at all). At any time you can cancel this auto-running task with self.after_cancel(self.job).
The buttons need to simply set or unset self.char. While it's unusual to use bindings rather than the command attribute for buttons, in this case that's exactly what you want.
Since the only difference between the buttons is what they send, all of the buttons can call the same function, passing in the character to be sent:
self.button1 = Button(self, text="Up")
self.button1.bind("<ButtonPress-1>", lambda: self.set_char('1'))
self.button1.bind("<ButtonRelease-1>", lambda: self.set_char(None))
def set_char(self, char):
self.char = char
Finally, you need to make sure you call send_data exactly once, and it will then run forever (or until cancelled with after_cancel)
class Application(Frame):
"""Defining the remote control buttons"""
def __init__(self,master):
...
self.char = None
self.send_data()
Ok, so this is from a larger project that I am working on, so I apologize if it looks messy.
The issue is that when I click the 'Exit Program' Button on the GUI, the window remains active.
I know that the button is working as when I hit the 'x' on the top right corner the window; the program closes, so the run variable has been set back to 0, which stops the code from looping.
My question is how do I get the window to be closed automatically when the exit button is clicked, because the root.destroy() method isn't doing it.
#imports
from tkinter import *
import random, pickle, shelve
#global vars
run = 0
class Window(Frame):
#the class that manages the UI window
def __init__(self, master, screen_type = 0):
"""Initilize the frame"""
super(Window, self).__init__(master)
self.grid()
if screen_type == 1:
self.log_in_screen()
def log_in_screen(self):
#Program Exit Button
self.exit = Button(self, text = " Exit Program ", command = self.end)
self.exit.grid(row = 3, column = 0, columnspan = 2, sticky = W)
def end(self):
global run, root
run = 0
root.destroy()
#Main Loop
def main():
global run, root
run = 1
while run != 0:
root = Tk()
root.title("Budget Manager - 0.6.1")
root.geometry("400x120")
screen = Window(root, screen_type = run)
root.mainloop()
store = shelve.open("store.dat", "c")
main()
store.close()
My question is how do I get the window to be closed automatically when
the exit button is clicked, because the root.destroy() method isn't
doing it.
The answer is: call destroy() on the root window. You say it isn't working, but the code you posted seems to work, and what destroy() is documented to do is exactly what you describe you want to have happen: it will destroy the window. Your code creates new toplevel windows in a loop, so maybe it only appears to not work since the old window id destroyed and the new window is created in the blink of an eye.
It seems like what you're really asking is "how can I make clicking on the "x" do the same as clicking on the "Exit program" button?". If that is the case, the answer is very straight-forward, even with your unconventional code that creates root windows in a loop.
To get the "x" button on the window frame to call a function instead of destroying the window, use the wm_protocol method with the "WM_DELETE_WINDOW" constant and the function you want it to call.
For example:
while run != 0:
root = Tk()
...
screen = Window(root, screen_type = run)
root.wm_protocol("WM_DELETE_WINDOW", screen.end)
...
root.mainloop()
you could do something like the below. I've used it in my own projects etcand it works.
Mywin =tkinter.Tk()
def exit():
Mywin.quit()
# etc.
When opening a new tkinter window, I only want the user to be able to click buttons on the new window. They should not be able to click on buttons from other windows that are part of the application. How would I accomplish this?
Here is a snip of my code:
def exportEFS(self):
self.exportGUI = Toplevel()
Button(self.exportGUI, text='Backup', command=self.backup).pack(padx=100,pady=5)
Button(self.exportGUI, text='Restore', command=self.restore).pack(padx=100,pady=5)
def backup(self):
self.backupWindow = Toplevel()
message = "Enter a name for your Backup."
Label(self.backupWindow, text=message).pack()
self.entry = Entry(self.backupWindow,text="enter your choice")
self.entry.pack(side=TOP,padx=10,pady=12)
self.button = Button(self.backupWindow, text="Backup",command=self.backupCallBack)
self.button.pack(side=BOTTOM,padx=10,pady=10)
In this snip, once the backupWindow is opened, the exportGUI remains open, but the user should not be able to click "Backup" or "Restore" while the backupWindow is opened.
Thanks!
You will want to call grab_set on the TopLevel window so that all keyboard and mouse events are sent to that.
def exportEFS(self):
self.exportGUI = Toplevel()
Button(self.exportGUI, text='Backup', command=self.backup).pack(padx=100,pady=5)
Button(self.exportGUI, text='Restore', command=self.restore).pack(padx=100,pady=5)
def backup(self):
self.backupWindow = Toplevel()
self.backupWindow.grab_set()
message = "Enter a name for your Backup."
Label(self.backupWindow, text=message).pack()
self.entry = Entry(self.backupWindow,text="enter your choice")
self.entry.pack(side=TOP,padx=10,pady=12)
self.button = Button(self.backupWindow, text="Backup",command=self.backupCallBack)
self.button.pack(side=BOTTOM,padx=10,pady=10)
What you can do is set the state to disabled. As so:
self.button.config(state="disabled")
And to enable it, you just use:
self.button.config(state="normal")
However, you must assign your buttons to variables first, like this:
self.backup=Button(self.exportGUI, text='Backup', command=self.backup)
self.backup.pack(padx=100,pady=5)
self.restore=Button(self.exportGUI, text='Restore', command=self.restore)
self.restore.pack(padx=100,pady=5)
so you would disable these using:
self.backup.config(state="disabled")
self.restore.config(state="disabled")
and re-enable using:
self.backup.config(state="normal")
self.restore.config(state="normal")
Please note however, that while the button is disabled, nothing can be changed to that button, both through the code, or through the user using it. So that means if you wanted to change the text of that button, you would have to change the state of the button to "normal" before changing it (if it already isn't in that state, which by default, all widgets are in that state when first created).
Cheers :)