Call a __init__ function from another function - python

I need some help form all of you, so i have something like this
class LoginFrame(Frame):
def __init__(self, master):
super().__init__(master)
...
...
...
def _login_btn_clickked(self):
...
...
result = messagebox.askyesno("Wrong username or password.", "Do you want to try again?")
if result == True:
self.__init__(self)
So if the user select the option to try again i need the whole program start again, and show the login box. But when i execute the program the process fail.

But when i execute the program the process fail.
Actually it should. __init__ method is normally called once an instance is created. You need to redesign this to either recreate an instance (first call pack_forget on that frame and then make new one and pack on that place), or do it using methods, like show_loginbox etc
Usually, loginboxes are made using Toplevel with Entry as fields and some buttons to post a result. If done so, on error you could simply erase login or/and passwords entry box and show an error
What I mean is:
reference implementation of above is here

Are you using tkinter.Frame?
I should think that it wouldn't like being reconstructed through the use of
super().init()
While already being packed into a root window (I'm adsuming that is the setup you have).

Related

Tkinter Class and Button use

I am having trouble finding a way to accomplish what I want using buttons in tkinter. I am programming a multi-frame tkinter app, for myself, and each frame is a class, each class has buttons that I place on the screen in the __init__ method. I am having trouble trying to link the buttons and the command functions together.
Example:
class frameHome:
def __init__(self,parent, controller)
self.frame=tk.Frame(parent)
self.buttonOne=tk.Button(self.frame,text="Click Me") # I want to add the command here
self.buttonOne.pack()
def buttonOneClick():
print("You clicked me")
When I add the command in the tk.Button() call it says buttonOneClick not defined. One video I watched said to add the function at the top of the code. I would like to keep the function as a method of the class for organization, as well as keep init at the top of the class and was wondering if there is a way to create a reference to the function so I can have it as a method after the the init method and be able to call it in the init method, because I would like the init method to create the gui and link all widget commands for that class/frame in it.
Thank you for any time and help that you can offer
in your command did you write the command as command=buttonOneClick()? if that's the case then you should replace it with self.buttonOneClick()
The answer I needed was that I forgot the self param in the buttonOneClick(self, msg) method.

Intercepting the close window button (Tkinter window) throws an Tcl error

I have a program that at some point opens a new window (filled with buttons and gizmo's for the user to select and play around with) that is defined as follows:
def window(self,master):
def close(self):
# change some variables
self.destroy()
top = self.top = Toplevel()
# Several lines of buttons
top.lift()
top.protocol("WM_DELETE_WINDOW",close(self))
I initially had a close button there that would wrap everything up nicely but I noticed that if the user used the standard 'X' in the corner of the window, this function obviously would not be called and that would give a lot of problems later on. I found out about the 'WM_DELETE_WINDOW' suggestion from some other questions on this website but it gives me a rather strange error:
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1630, in wm_protocol
'wm', 'protocol', self._w, name, command)
TclError: bad window path name ".33862072"
I assume that it somehow has gotten the wrong window ID and is unable to catch the event. My question is thus, is that true or not and secondly how should I continue to deal with this issue.
Let's examine this line of code:
top.protocol("WM_DELETE_WINDOW",close(self))
This line of code is saying "immediately call the function close(self), and assign the result to the protocol handler. See the problem? It's immediately calling close, likely before self has been fully constructed. You don't want the function to be called, you want to pass in a reference to the function.
Make close be a method of self (rather than an embedded function) and change the call to top.protocol to look like this (note the lack of trailing parenthesis):
top.protocol("WM_DELETE_WINDOW", self.close)
If you prefer to keep the nested function, you can use lambda:
top.protocol("WM_DELETE_WINDOW", lambda window=self: close(window))

Create a python tkinter window with no X (close) button

I'm writing a 'wizard' type Python Tkinter GUI that collects information from the user and then performs several actions based on the user's entries: file copying, DB updates, etc. The processing normally takes 30-60 seconds and during that time, I want to:
Provide the user with text updates on the activity and progress
Prevent the user from closing the app until it's finished what it's doing
I started on the route of having the text updates appear in a child window that's configured to be trainsient and using wait_window to pause the main loop until the activities are done. This worked fine for other custom dialog boxes I created which have OK/cancel buttons that call the window's destroy method. The basic approach is:
def myCustomDialog(parent,*args):
winCDLG = _cdlgWin(parent,*args)
winCDLG.showWin()
winCDLG.dlgWin.focus_set()
winCDLG.dlgWin.grab_set()
winCDLG.dlgWin.transient(parent)
winCDLG.dlgWin.wait_window(winCDLG.dlgWin)
return winCDLG.userResponse
class _cdlgWin():
def __init__(self,parent,*args):
self.parent = parent
self.dlgWin = tk.Toplevel()
self.userResponse = ''
def showWin(self):
#Tkinter widgets and geometry defined here
def _btnOKClick(self):
#self.userResponse assigned from user entry/entries on dialog
self.dlgWin.destroy()
def _btnCancelClick(self):
self.dlgWin.destroy()
However this approach isn't working for the new monitor-and-update dialog I want to create.
First, because there's no user-initiated action to trigger the copy/update activities and then the destroy, I have to put them either in showWin, or in another method. I've tried both ways but I'm stuck between a race condition (the code completes the copy/update stuff but then tries to destroy the window before it's there), and never executing the copy/update stuff in the first place because it hits the wait_window before I can activate the other method.
If I could figure out a way past that, then the secondary problem (preventing the user from closing the child window before the work's done) is covered by the answers below.
So... is there any kind of bandaid I could apply to make this approach work the way I want? Or do I need to just scrap this because it can't work? (And if it's the latter, is there any way I can accomplish the original goal?)
self.dlgWin.overrideredirect(1) will remove all of the buttons (make a borderless window). Is that what you're looking for?
As far as I know, window control buttons are implemented by the window manager, so I think it is not possible to just remove one of them with Tkinter (I am not 100% sure though). The common solution for this problem is to set a callback to the protocol WM_DELETE_WINDOW and use it to control the behaviour of the window:
class _cdlgWin():
def __init__(self,parent,*args):
self.parent = parent
self.dlgWin = tk.Toplevel()
self.dlgWin.protocol('WM_DELETE_WINDOW', self.close)
self.userResponse = ''
def close(self):
tkMessageBox.showwarning('Warning!',
'The pending action has not finished yet')
# ...

Handling events from file menu

I am trying to code a program with a regular file menu. (e.g. File, Edit, View, etc).
I want the action they take in that menu to update my status bar (a label).
The problem is, the way I have it setup now, I believe it's executing the command and then trying to take the result as what it should do.
Currently a menu item is defined like so:
fileMenu.add_command(label="Insert", command=self.statusUpdater(statusLabel,"Insert Triggered")
And the function statusUpdater is defined as such:
def statusUpdater(self,status,commandName):
status.config(text=commandName)
status.update_idletasks()
So the problem is, right at the start of the program, the status changes to "Insert Triggered". What I want is for that to only happy once I have actually clicked "Insert"
From hints I've seen elsewhere it seems like I need some way to pass and handle the event of Insert being clicked.
Could someone supply a generic and basic function that does what I ask? I think the problem lies in the () attached to the command function, but I don't know any other way to pass arguments.
All i need is a function that is called on the click event, and knows which fileMenu command triggered it.
Thanks!
Commands take a reference to a function. You can se a lambda if you want to pass it arguments:
...command=lambda l=statusLabel, self.statusUpdater(l, "Insert Triggered"))

How can I properly read out spin buttons in pyGTK?

For a small timer app I want to write a GTK interface where I can set the desired time. Here is a picture of the interface:
However, I am having trouble reading out the fields of the spin buttons. My envisaged procedure for this is the following:
Read out the buttons using methods for each button
Here is one of the methods that does this:
# Get the fields of the spinbuttons
def get_seconds(self, widget, spin):
self.rSeconds = spin.get_value_as_int()
It is then called like this:
button = gtk.Button("Start")
button.connect("clicked", self.get_seconds, spinnerS)
Create a timer object with the data from the buttons
This is planned to be accomplished using this method:
# Create the timer object ...
def prepare_timer(self, widget, hours, minutes, seconds, title, text):
self.timer = eggTimer(hours, minutes, seconds, title, text)
Which is called here:
button.connect("clicked", self.prepare_timer, self.rHours, self.rMinutes, self.rSeconds, "some title", "some text")
Unfortunately, when running the script I get the following error message:
Traceback (most recent call last):
File "GTKInterface.py", line 140, in <module>
SpinButtonExample()
File "GTKInterface.py", line 126, in __init__
button.connect("clicked", self.prepare_timer, self.rHours, self.rMinutes, self.rSeconds, "Title", "Text")
AttributeError: SpinButtonExample instance has no attribute 'rSeconds'
To check whether there really is no instance of that variable, I programmed a short method to print it:
def returnS(self, widget):
print self.rSeconds
And surprisingly this method can "see" self.rSeconds. This makes me wonder what determines the visibility of the variable. What am I doing wrong to read this out?
You try to pass the attribute self.rHours to the connect method, but at that point the attribute doesn't exist yet (the clicked handlers haven't executed yet).
Note that even if you fill in self.rHours before calling connect, it will pass the value at the time of connecting, not at the time of the handler executing.
You can solve this by passing self.rHours etc directly to eggTimer in prepare_timer.
But it would be even easier to just combine all the click handlers into one, and use local variables instead of self.rHours etc. There's no reason to split your code over many click handlers like this.
Edit: BTW, you can also use nested functions instead of methods:
...
def prepare_timer(widget):
self.timer = eggTimer(
spinnerH.get_value_as_int(),
spinnerM.get_value_as_int(),
spinnerS.get_value_as_int(),
"Title", "Text")
button.connect("clicked", prepare_timer)
...
Keep it simple!
Going off of adw's answer recommending one click handler, a simple addition to your pastebin code would be:
def read_and_prepare(self,spinnerS,spinnerM,spinnerH,title,text):
self.get_seconds(spinnerS)
self.get_minutes(spinnerM)
self.get_hours(spinnerH)
self.prepare_timer(elf.rHours, self.rMinutes, self.rSeconds, title, text)
and only have
button.connect("clicked", self.read_and_prepare,spinnerS,spinnerM,spinnerH,"Title","Text")
for the connection code.
You could also probably redesign a bit to avoid all the get_* calls and just read the values in the click handler.

Categories