I have a small program that batch handles files. These files use a map file to load certain settings. The map file has a line at the top that specifies for what directory it is for.
Currently I am able to read the line and assign it to the source path variable (sPath). I want to update the TextCtrl for the Source Directory, however it is in the MainFrame class and I load the map file in a different class.
class Process(wx.Panel):
def loadMap(self, event):
MainFrame.sPath = str(mapFile.readline()).strip("\n")
MainFrame.loadSource(MainFrame())
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title="DICOM Toolkit", size=(800,705))
self.srcTc = wx.TextCtrl(self.panel, 131, '', size=(600,25), style=wx.TE_READONLY)
def loadSource(self):
self.srcTc.SetValue(MainFrame.sPath)
I eliminated most of the code and what's above is where it is giving me trouble. How do I change self.srcTc in the MainFrame class from either the Process class or a function in the MainFrame class? I am having trouble actually pointing to self.srcTc without a handler that stems from the MainFrame class.
There are several ways to accomplish this sort of thing. You can pass a handle to your panel class that can call whatever it needs in the parent to set the value (i.e. parent.myTxtCtrl.SetValue(val) ) or you can use pubsub. I personally recommend the latter as it's much more flexible and less prone to breakage as you change your program. I wrote the following tutorial that should get you up to speed: http://www.blog.pythonlibrary.org/2010/06/27/wxpython-and-pubsub-a-simple-tutorial/
I think what you want has to look like something like that (without a working example):
class Process(wx.Panel):
def loadMap(self, event):
frame = MainFrame()
frame.sPath = str(mapFile.readline()).strip("\n")
frame.loadSource()
when using MainFrame.sPath = ... you're not actually changing sPath to a MainFrame you created, but to the class itself, then you create it, in MainFrame() without storing a reference to it (assign it to a variable for example). So, you can't access it from somewhere other than "inside" the class itself as self.
The solution is to create an instance of a MainFrame and operate on it. Once you create it and assign it to a variable, you can manipulate the .sPath attribute and call loadSource().
UPDATE: From you code snippet, it seems you create the MainFrame instance in the end of the file: MainFrame().Show(), and then in the loadMap method, you create a new one.
What you should do is this, in the end of your file:
app = wx.App(0)
#MainFrame().Show()
mainFrame = MainFrame() # or, insteadof making it a global variable, pass it as an argument to the objects you create, or store a reference to it anywhere else.
mainFrame.Show()
app.MainLoop()
and in the loadMap method:
def loadMap(self, event):
global mainFrame # or wherever you stored the reference to it
# ...
# remove this:
# mainFrame = MainFrame()
# set the sPath to the OBJECT mainFrame not the CLASS MainFrame
mainFrame.sPath = str(mapFile.readline()).strip("\n")
mainFrame.srcTc.SetValue(MainFrame.sPath)
Now this way, it should work.
The problem was that you are creating another frame, changing its path and updating its text, but you are not showing it. The correction is to store the actual window that is being shown, and update this one.
Related
How can I change an attribute on the object when the attribute is Frame? I want to change the color of the frame.
class MyFrame:
def __init__(self, bg_color)
self.window = tk.Frame(self.parent, bg=bg_color)
mainApp:
frame_obj = MyFrame("blue")
#want to change the color after the frame obj has been created
frame_obj.window.bg = "red" #this does not work
Tkinter is based on different progamming language, named tcl and thats why things seem a bit unpythonic here.
The tk.Frame object isnt a pure python object, rather its an wrapped object in a tcl interpreter and thats why you cant address the attributes you intuitivly think you can. You need to adress the attributes in a way the tcl interpreter is abel to handle and therefor methods are created like widget.configure.
To achive what you want with your current code, it would look like:
import tkinter as tk
root = tk.Tk()
class MyFrame():
def __init__(self, bg_color):
self.window = tk.Frame(width=500,height=500,bg=bg_color)
frame_obj = MyFrame('blue')
frame_obj.window.pack(fill=tk.BOTH)
frame_obj.window.configure(bg='yellow')
root.mainloop()
In addition, the proper way for an OOP approach for a frame would look like this:
class MyFrame(tk.Frame):
def __init__(self,master,**kwargs):
super().__init__(master)
self.configure(**kwargs)
frame_obj = MyFrame(root,width=500,height=500,bg='green')
frame_obj.pack(fill=tk.BOTH)
This way your class becomes a child of the tk.Frame object and you can adress it directly. The syntax self in this context refers directly to the tk.Frame object. Also it is good practice to use the format of
def __init__(self,master,**kwargs):
while its the same for the parent/tk.Frame. It has a single positional argument, named master and keywords arguments for the configuration of the Frame.
Please take a look at a tutorial for tkinter and for OOP. If you had you would know that. Please dont feel offended, but StackOverflow requiers a brief reasearch and that includes to read documentation and take tutorials.
I've written a program and I'm learning about classes and thus, rewriting the code to take advantage of classes and objects.
My problem is if I create a tkinter widget "dropdown menu" from a class, I can not retrieve the get.state variable.
class ...:
def make_drop_menu(self,parent,drop_opts,drop='empty',state=''):
self.parent = parent
self.drop = drop
self.drop_opts = drop_opts
self.state = tk.StringVar()
self.state.set(self.drop_opts[0]) # sets default value on drop (drop down menu)
self.drop = OptionMenu(self.parent, self.state, *self.drop_opts) # completed drop
return self.drop
----------------
class main:
self.widget = self.var.make_drop_menu(self.obj_mainwindow.btm_frame, self.col_opts)
Added for clearity:
window = windowclass() #windowclass is the style for tkinter. Makes a tkwindow.
self.widget = self.windowclass.var.make_drop_Menu() #
I've left out some code to simplify. But my main problem is that I create a widget called self.widget which builds my drop down menu. However, I have NO idea how to get the state of the drop down.
Hopefully the code makes sense.
If your class is named MyClass, then to access the state variable you would use the instance of the class. In your case it appears that self.var is the instance of your class, so it would look something like this:
self.var = MyClass()
self.var.make_drop_menu(...)
...
print(self.var.state.get())
Made the make_drop_menu class independent of any other class object. That way I could make a drop menu, assign the parent tk window, and create a method for getting the state variable when called.
Thanks to Bryan Oakley for the help!
I have been trying to use classes for both of my files. I made a gui.py with:
class GuiStart:
def __init__(self, master):
self.master = master
self.master.title("DECTools 1.3")
I have another file with methods I want to execute. This file is called foo.py
class DecToolsClass:
def __init__(self):
self.gui = gui.GuiStart()
I get an error, because I don't give the it the master parameter. I can't set it to None because it doesn't have the .title method.
I execute the gui file with:
if __name__ == "gui":
root = tkinter.Tk()
my_gui = GuiStart(root)
root.mainloop()
The problem is that I need to execute a method from foo.py with my gui.py file and I need to access attributes from my gui.py file with my foo.py file. I have been trying to accomplish this and I know I can't use multiple constructors like in Java.
Is it possible what I want or do I have to rewrite my code?
Thanks in advance!
The GuiStart class starts the tkinter gui. The window with buttons and entries is created with that class. From the GuiStart class I call methods that do things like copy files to a certain location
Alright, so to sum it up, you have a class that handles user interaction, and a set of generic methods doing no user interaction, that GuiStart provides a Gui for. If I understand wrong, this answer will be much less useful.
It is indeed a good idea to split those, but for this split to be effective, you must not have direct references from one another. This means this is a definitive DON'T:
class DecToolsClass:
def __init__(self):
self.gui = gui.GuiStart()
If you actually needed the tools to access the gui, you'd inject it. But normally you would want it the other way around: Tools should be generic and not know about Gui at all. Onn the other hand, Gui knows about them. Assuming the rest of the code is correct (I don't know tkinter):
def main():
tools = DecToolsClass() # not shown, but it no longer has self.gui
root = tkinter.Tk()
my_gui = gui.GuiStart(root, tools)
root.mainloop()
if __name__ == '__main__':
main()
Which means of course GuiStart must take the toolset it will use as an argument:
class GuiStart:
def __init__(self, master, tools):
self.master = master
self.master.title("DECTools 1.3")
self.tools = tools
Now, everywhere in GuiStart, any use of tools must go through self.tools. As an added bonus, in your unittests you can pass a dummy tools object that just checks how it is called, that makes testing very easy.
I have a big problem using tkinter with self here is my code
Could people please give an answer, thanks! The error I get is something like,self could not be given a variable outside a function.
from tkinter import *
root = Tk()
class start():
global self
self = root
def __init__():
self.title('__init__')
self.geometry('300x300')
__init__(self)
class window_extra():
def canvas(self):
global self
selfc = Canvas(self, bg='black').pack()
canvas(self)
self.mainloop()
Thanks!
You should not use self as a variable name as it is used to specify if something is an attribute of the instance of the class.
You do not need to use global in classes either as class attributes are used in most cases when dealing with variables that are needed through the class.
Judging by the code you have shown I think you are trying to do something like this:
from tkinter import *
class start():
def __init__(self, root):
self.master = root
self.master.title('__init__')
self.master.geometry('300x300')
Canvas(self.master, bg='black').pack()
root = Tk()
start(root)
root.mainloop()
However I believe you are struggling with the OOP method of programing and I would suggest to not use OOP to start with if this is the case.
Maybe take a few tutorials on youtube or hit up Codecadamy.
In response to your comments:
In my Opinion using init properly is a bad idea. I use it as a regular def. I doesn't matter if I use self global, unless the function/class variable is called self.
I respect the proper use of init, but I just find the whole thing with, init and self.master I just don't get any of it!
Lack of understanding a thing does not mean said thing is bad. The use of self.master is there to provide a class attribute that ties back to the root Tk() variable. This allows any method within the class to interact with the instance of Tk(). I can't speak to other programing languages but the use of self is a very important in OOP for python. It may not be 100% required to reserve self for referencing to either the instance of the object or the class attribute but it is the accepted and known use of self and really should not be changed/overwritten.
I restructured for some simplicity, but I think that you need a better understanding of objects in Python before going too much further down the GUI route. I think that you mean something like this:
from tkinter import *
# creates a subclass of Tk() called 'Application'; class names by convention
# use CamelCase; best to stick with convention on this one
class Application(tkinter.Tk):
# you don't have to create an explicit invocation of '__init__', it
# is automatically run when you instantiate your class (last line)
def __init__():
super().__init__() # initialize the super class (the 'tkinter.Tk()')
self.title('__init__') # use 'self' to refer to this object - do not declare it as global! it is only valid within the object!
self.geometry('300x300')
self.my_canvas = tkinter.Canvas(self, bg='black') # save an instance of your canvas for easy reference later
self.my_canvas.pack() # pack as a separate step (only required if you plan to use the canvas later... or ever)
self.mainloop() # begin the tkinter loop
# this 'if __name__ ...' is a good idea in most cases, allows you to import `Application` into other
# files without actually running it unless you want to from that other file
if __name__ == '__main__':
Application() # start your class
How do I refer to the widget values in the following code.Here I have added widgets by calling methods in the app class for different frames.Next,I want to access the values in all the Widgets(which the user enters) of all the frames at the same time.But I am not able to figure out how should I refer to them and access their values!
class myapp():
def __init__(self,parent):
self.parent=parent
self.container=Frame(self.parent)
self.container.pack()
self.tab1=Button(self.container,text='tab1',command=self.tab1Click)
self.tab2=Button(self.container,text='tab*emphasized text*2',command=self.tab2Click)
self.tab1.pack()
self.tab2.pack()
def tab1Click(self):
top=Toplevel()
self.container1=Frame(top)
self.add_widget1(self.container1)#self.add_widgeti(parent) is a method in myapp() class to add a widget to a frame
self.add_widget2(self.container1)
self.add_widget3(self.container1)
self.container1.pack()
def tab2Click(self):
top=Toplevel()
self.container2=Frame(top)
self.add_widget2(self.container2)
self.add_widget4(self.container2)
self.add_widget5(self.container2)
self.container2.pack()
def write(self):
#here I want to write the values contained in the widgets in both frames in a file,but I am not able to figure out how do I refer to them and access their values.
Any help will be highly appreciated.Thanks in advance.
The widgets in which the user can write have a get method that returns their content. But in order to do this, you need to store your widget in a class variable for example.
Edit: I had misunderstood the problem and I hadn't realized that the add_widget function would be called for different containers for the same instance. One way to keep track of all created widgets is to create a widget list:
add self.widgets = [] in __init__
define theadd_widget method like that:
def add_widget(self, container):
self.widgets.append(Entry(container, text="enter text here"))
self.widgets[-1].pack()
Then to get the text entered by the user in all widgets (inside the write function):
texts = []
for widget in self.widgets:
texts.append(widget.get())