Call functions within event handler in Python Turtle - onclick

I am trying to call the startagain function within the onclick event handler. The function does execute the input statement but does not allow the user to enter a response, the program just terminates.
import turtle
wn = turtle.Screen()
#Event Handler
def clicked(x,y):
print(x,y)
startagain()
#Start over
def startagain():
again = input("Want to start over? ")
if again == "y":
print("okay")
#The event
wn.listen()
wn.onclick(clicked)
wn.mainloop()
I'm not sure if I need to somehow stop the screen from listening or if there is another approach to this. Any help would be appreciated.

Your code works for me as-is. However, I would make two changes. First, I would have clicked() call startagain() indirectly via ontimer() (with no time delay), so that we don't end up with multiple clicked() calls on our stack. I would also obtain input via textinput() rather than switch back to the console for input:
from turtle import Screen, Turtle
# Event Handler
def clicked(x, y):
screen.ontimer(startagain)
# Start over
def startagain():
again = screen.textinput("Game Over", "Want to start over? ")
if again and again.lower().startswith("y"):
print("okay")
# The event
screen = Screen()
screen.onclick(clicked)
screen.mainloop()

Related

How to remove ComboBox selection using button (Julia Gtk)?

I am trying to do a simple GUI using Gtk with the Julia programming language, however, when I try to get the button to remove the active selection in the combobox programmatically, I get an "AssertionError".
"ERROR: AssertionError: xor(prev, current_task() !== g_stack)"
I am not sure how to get this simple example to work ?
Can anyone point me in the right direction ?
Here is my non-functional code:
using Gtk
# Create widgets------------------------------------
cb = GtkComboBoxText()
button = GtkButton("Remove Active")
# Create and Add choices to ComboBox ---------------
choices = ["zero", "one", "two", "three", "four"]
for choice in choices
push!(cb,choice)
end
# Function to get the selected choice (text) from the ComboBox
function getChoice()
i = get_gtk_property(cb, "active", Int)
return choices[i+1]
end
# Function that handles the ComboBox selection change---
function selection_changed(widget)
sel = getChoice()
println("We selected: $sel")
end
# Function to handle the button press------------------
function removeChoice(widget)
set_gtk_property!(cb,:active,-1)
end
# Connect the signals to the widgets -------------------
signal_connect(selection_changed, cb, "changed")
signal_connect(removeChoice, button, "clicked")
# Create window, and add widgets to it using Box Layout
win = GtkWindow("ComboBoxText Example",200,50)
vbox = GtkBox(:v)
push!(win, vbox)
push!(vbox, cb)
push!(vbox, button)
showall(win)
Note the warning at the end of this Gtk.jl manual page:
Warning: it is essential to avoid task switching inside Gtk callbacks, as this corrupts the Gtk C-stack. For example, use #async print or queue a message for yourself. ...
if you are still seeing segfaults in some random method due to there existing a callback that recursively calls the glib main loop (such as making a dialog box) or otherwise causes g_yield to be called, wrap the faulting code in GLib.#sigatom. This will postpone execution of that code block until it can be run on the proper stack (but will otherwise acts like normal control flow).
This is what happens here, when you try to change the selection status of the combo box from with a signal-handler callback - the "callback that recursively calls the glib main loop" as the manual page calls it.
Using either #async or Gtk.GLib.#sigatom in front of the set_gtk_property! call avoids this problem and allows the code to run.
In this case, that leads to a different error message because removeChoice itself leads to selection_change being called, and the getChoice call made there does not take into account that get_gtk_property(cb, "active", Int) could return -1. So we get a BoundsError. How you fix that depends on your use case, for demo purposes I just return nothing here in that case:
# Function to get the selected choice (text) from the ComboBox
function getChoice()
i = get_gtk_property(cb, "active", Int)
return i >= 0 ? choices[i+1] : nothing
end
# Function that handles the ComboBox selection change---
function selection_changed(widget)
sel = getChoice()
println("We selected: $sel")
end
# Function to handle the button press------------------
function removeChoice(widget)
#async set_gtk_property!(cb,:active,-1)
end
Running this, the output as I select two, then "Remove Active", then four, then "Remove Active" again in the GUI is:
julia> We selected: two
We selected: nothing
We selected: four
We selected: nothing

PyQt signal to track when row in QTableView was moved

I am using a subclassed version of QAbstractItemModel with a QTableView and have drag-and-drop activated with a subclassed model.dropMimeData(), model.insertRows(), model.removeRows(). Now I want to show the changes after a drag-and-drop operation is finished and offer the user to undo the operation again. I therefore implemented my own dropEvent()-method for the tableView. I also set the move method to InternalMove.
I check for a confirmation of the move inside the method and then call super(widget.__class__, widget).dropEvent(event). I would expect that after this execution the row was inserted at the new position and deleted at the old position. What happens is that it inserts the row at the specified position, but it deletes row at the old position only after dropEvent() is finished. It does not matter if I call event.accept() or event.acceptProposedAction() inside the function, it always waits until dropEvent() finishes.
I am looking for a signal that tells me when a drag-and-drop operation was executed. I would expect QAbstractItemModel's rowsMoved-signal to be what I want, but it is not emitted during the dnd-operation. The signals rowsInserted and rowsRemoved are however emitted. But the rowsRemoved signal is just emitted as soon as dropEvent() finishes. Does anybody know where QTableView executes the insertion of the target row, setting of the data and the removal of the source row?
I am using python3 with PyQt5 on Windows 10.
def dropEvent_withConfirm(widget, event):
dropInd = widget.indexAt(event.pos())
if not dropInd.isValid():
event.ignore()
return
confirmGUI = ConfirmGUI("Drag Element to new position?",
"Are you sure you want to drag the element to this position?",
True)
if confirmGUI.getConfirmation():
super(widget.__class__, widget).dropEvent(event)
event.accept()
# Here I want to do something after the finished move, but here the target row was inserted, but the source row is not yet deleted
else:
event.ignore()
self.client.tblView.dropEvent = lambda e: dropEvent_withConfirm(self.client.tblView, e)
I solved it now by not relying on the super().dropEvent(), but by implementing it myself. I couldn't find a fitting signal that is emitted on finalization of the drop. Down below is my updated code:
def dropEvent_withConfirm(widget, event):
dropInd = widget.indexAt(event.pos())
if not dropInd.isValid():
event.ignore()
return
confirmGUI = ConfirmGUI("Drag Element to new position?",
"Are you sure you want to drag the element to this position?",
True)
if confirmGUI.getConfirmation():
old_row, new_row = getSrcAndDstRow()
entry = getDraggedEntry()
self.myModel.insertRows(new_row)
self.myModel.setRow(new_row, entry)
if old_row > new_row:
self.myModel.removeRows(old_row + 1)
else:
self.myModel.removeRows(old_row)
event.accept()
# Here I can now do something after the finished move
else:
event.ignore()
self.client.tblView.dropEvent = lambda e: dropEvent_withConfirm(self.client.tblView, e)

How to use three different tkinter buttons with one command

Please house help. When using tkinter, I find it difficult to call a defined function in a button when the def is below the button. I use python 3.6.9. Example
import tkinter
window = tkinter.Tk
button = tkinter.Button(window, text="hello",command=newpage()).grid(column=0, row=0)
def newpage():
new = tkinter.toplevel()
The button does not work except I use lambda and also the lambda does not work if I define something new under the button. The new definition blocks the lambda from seeing the other def.
NB: I use the lambda like this lambda:newpage()
NB: I use python 3.6.9
Also please how can I make many tkinter buttons to use the same command (as in def)
The newpage function should really be defined before being used, just as a good programming practice if for no other reason.
The variable window is being set to tkinter.Tk instead of the object returned by tkinter.Tk().
The button is calling the function immediately because command expects a function to run, and thinks you want it to run something returned by newpage, instead remove the () command=newpage.
Toplevel is capitalized, tkinter.Toplevel().
A function can be used by any button, just assign the command to use the function:
import tkinter
def newpage():
new = tkinter.Toplevel()
window = tkinter.Tk()
button1 = tkinter.Button(window,
text="hello1",
command=newpage).grid(column=0, row=0)
button2 = tkinter.Button(window,
text="hello2",
command=newpage).grid(column=0, row=1)
button3 = tkinter.Button(window,
text="hello3",
command=newpage).grid(column=0, row=2)

Why does the "command" directly open and not when clicked on Button? Python 3

I pretty new in this whole Python thing and my question is how to make, that a button runs the command, when clicking it and not before.
I searched much in the Internet but i didnt find anything.
I dont understand the classes at all. Is there no other way to do this?
Here is my work, i did on the programm.
Thanks for your help
from tkinter import *
import os
t = ""
def ordner(x):
print ("def")
if os.path.exists(os.path.join("/Kunden/",x)) == True:
pass
else:
os.mkdir(os.path.join("/Kunden/",x))
def E1holen():
x = E1.get()
ordner(x)
#Hauptfenster
main=Tk(className='Kundendatenbank')
main.iconbitmap('icon.ico')
#Inhalt Hauptfenster
L1 = Label(main, text="Kundenname:")
L1.pack(side = LEFT)
E1 = Entry(main, bd =5, textvariable=t)
E1.pack(side = RIGHT)
a = Button (main, text=("erstellen/bearbeiten"), command=E1holen()).pack()
main.mainloop()
It runs immediately ecause you tell it to.
What is the syntax for calling a function in Python? It's foo(), right? So, when you do command=E1holen(), what should python do? It should call E1holen(), and then pass the result to the command attribute.
Put another way, the command attribute takes a reference to a function, but because of the () you were calling the function and giving the command attribute whatever that function returned. The solution? Remove the ():
a = Button(..., command=E1holen)

Pyjs / Pyjamas Frame: How to use a button to change the url in a frame

I have a web page that contains two buttons and a frame. Within the frame, a web page is displayed. I am trying to make it so button A shoes url '/AAA' in the frame while button B shoes url '/BBB' in the frame. How the heck can I do that?
Here is what I have:
class ImageButton(SimplePanel):
def __init__(self, image_location):
'''(None, str) -> None'''
SimplePanel.__init__(self)
img = Image(image_location, StyleName='rangler')
img.addClickListener(getattr(self, "onImageClick"))
self.add(img)
def onImageClick(self, sender=None):
pass
#This is where I need help!?
class QAFrame(SimplePanel):
def __init__(self, current_url):
SimplePanel.__init__(self)
frame = Frame(current_url,
Width="200%",
Height="650px")
self.add(frame)
def caption():
style_sheet = HTML("""<link rel='stylesheet' href='about_us.css'>""")
srah_pic = ImageButton("Steve.jpg")
fl_pic = ImageButton("Fraser.jpg")
horizontal = HorizontalPanel()
vertical = VerticalPanel()
vertical.add(srah_pic)
vertical.add(fl_pic)
horizontal.add(vertical)
QAFrame('Fraser_qa.htm')
QAFrame('Steve_qa.htm')
horizontal.add(QAFrame('Steve_qa.htm'))
RootPanel().add(horizontal)
Basically,
You need to .addClickListener to your button and, as a parameter, you want to pass in the handler that will perform your desired task on button click.
The one thing that really confused me was, I could not pass an argument through to my handler. But, the object "sender" is automatically passed in with the handler. You can try to fish through the senders attributes to find information that you need.
class ImageButton(SimplePanel):
def __init__(self, image_location, css_style):
'''(None, str, str) -> None'''
SimplePanel.__init__(self)
self.image_location = image_location
img = Image(self.image_location, StyleName= css_style)
img.addClickListener(Cool) # Cool is the name of my handler function
self.add(img)
def Cool(sender): # You can do whatever you want in your handler function.
# I am changing the url of a frame
# It is a little-medium "Hacky"
if sender.getUrl()[-9:] == 'Steve.jpg':
iframe.setUrl('Fraser_qa.htm')
else:
iframe.setUrl('Steve_qa.htm')
I would extend my ImageButton class to support passing in the URL to the webpage you want to show. In the __init__ function, you can store that URL in an instance property.
You should make the clickhandler into an instance method, which can access the instance variable which holds the URL to the desired page.
I lack the definitive Python knowledge to supply code examples. Hope you still understand the concept.

Resources