I am trying to make a multiple "page" GUI using tkinter and couldn't really find a good method. I resorted to using grid_forget and seperating the "pages" in methods and it works like a charm. However I am trying to seperate the methods into a seperate file to make everything a little cleaner but I keep getting a global name error upon trying to run one of the methods with all of the widgets from my main file/class.
Traceback (most recent call last):
File "C:/Users/Derek/PycharmProjects/whites/Main.py", line 3, in <module>
from screens import *
File "C:\Users\Derek\PycharmProjects\whites\screens.py", line 4, in <module>
import Main
File "C:\Users\Derek\PycharmProjects\whites\Main.py", line 54, in <module>
app = Application(master=main, w=main.winfo_width(), h=main.winfo_height())
File "C:\Users\Derek\PycharmProjects\whites\Main.py", line 20, in __init__
home_screen(self,True)
NameError: global name 'home_screen' is not defined
I also tried importing it such as import screens and tried to run screens.home_screen(self,True) and that yeilds
AttributeError: 'module' object has no attribute 'home_screen'
Even though it does
Example Main.py
from screens import *
import globals
class Application(tk.Frame):
globals.init()
def __init__(self, w, h, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.window_width = w
self.window_height = h
home_screen(self,True)
query_screen(False)
res_screen(False)
settings_screen(False)
screens.py
import tkinter as tk
import globals
import Main
def home_screen(self, state):
*define widgets for this "screen"
[EDIT]
Here is a copy of the full files.screens , Main , globals
[EDIT: 2]
So I changed the code around to try a different solution and it basically the same error. module object has no attribute if I attempt to convert the screens.py into a class and initialize it as an object but nothing. So I am guessing this means it is not python at all and more my project settings have somewhere gone askew
You define:
def home_screen(self, state):
but it's not in any class!
Remove the self from the function signature and call it using only state variable only
EDIT:
If you create main.py:
from screens import *
class Application():
def __init__(self):
home_screen(True)
# then later on
a = Application() # this will print 'True'
and in file screens.py:
def home_screen(state):
print state
it will work (assuming both are in the same directory).
The solutions was to run the screens.py and not the main.py. Why that is I have no idea but it still ultimately fits my goal. If anyone would like to weigh in go for it.
[EDIT]
Final solution, change the way the program starts.It now runs just fine.
if __name__ == "__main__":
main = tk.Tk()
main.wm_title("White's Canoe Livery")
main.state("zoomed")
main.update()
# print(main.winfo_width())
app = Application(master=main, w=main.winfo_width(), h=main.winfo_height())
app.mainloop()
Related
I'm writing a code on PyCharm, but it is easier for me if I could debugg continually while I'm writing my code. I can't figure out, that how do I get this class debugged? :
from PyQt5.Qt import QGraphicsPixmapItem
from PyQt5.QtGui import QPixmap
class Level():
def __init__(self, scene, level):
self.scene = scene
self.level = open(level)
def add_item_to_scene(self):
for x in range(self.level):
for y in range(self.level[0]):
if y == 'X':
brick = QPixmap('brick.png')
self.scene.addItem(brick)
I have just begun and this is all what I've written. I'm doing a Platformer game with PyQt5, and I'm trying to set the levels now. Thank you for any help.
Use a convention to run code during development and not run the code on import as shown in the following code.
from PyQt5.Qt import QGraphicsPixmapItem
from PyQt5.QtGui import QPixmap
class Level():
def __init__(self, scene, level):
self.scene = scene
self.level = open(level)
def add_item_to_scene(self):
for x in range(self.level):
for y in range(self.level[0]):
if y == 'X':
brick = QPixmap('brick.png')
self.scene.addItem(brick)
# Use the following convention.
# In python the initial module run receives the name __main__.
if __name__ == '__main__':
print(f'Hello my name is {__name__=} because my module was started by '
f'pyhton. ')
# So while developing whenever you run this module commands here will run.
# Later on when you import the module the commands will not run.
# because __main__ will be the name of this module
l = Level('brick', 5)
l.add_item_to_scene()
else:
# Place any commands you want to run on import here.
print(f'Hello my name is {__name__=} because my module was imported by '
f'another module. ')
Demo Results:
If run directly from python
Hello my name is __name__='__main__' because my module was started by pyhton.
Traceback (most recent call last):
File "C:\Users\ctynd\OneDrive\CodeBase\StackOverflowActivity\OldScratches\scratch_2.py", line 27, in <module>
l = Level('brick', 5)
File "C:\Users\ctynd\OneDrive\CodeBase\StackOverflowActivity\OldScratches\scratch_2.py", line 9, in __init__
self.level = open(level)
OSError: [WinError 6] The handle is invalid
If imported:
import scratch_2
Results
Hello my name is __name__='scratch_2' because my module was imported by another module.
Edit: This question was on the assumption that I could import parts of modules without importing the whole module. It turns out that isn't the case so I decided to just import the whole module with from ex45_extras import * anyway. That makes this question pointless, but I decided to not delete this question so that some other beginner with the same question can come here and find out the sad truth: You can't import modules as parts anyways
The following is the original question:
I'm a beginner so sry for the noob question. I want to call specific classes from a module with a variable. I don't want to call the whole module. And I need to do this using this class Nav() to control which classes are being imported. Is there a way to do this? Or is there a better solution?
class Nav():
def __init__(self):
print("This is class Nav")
def play(self):
current_scene_name = "A()" # this is where the variable is defined
from ex45_extras import current_scene_name # <- this is the one
x = Nav()
x.play()
Currently it's raising this error:
This is class Nav
Traceback (most recent call last):
File "D:\Programming\Python\LPTHW_Exs\ex45\test.py", line 11, in <module>
x.play()
File "D:\Programming\Python\LPTHW_Exs\ex45\test.py", line 7, in play
from ex45_extras import current_scene_name
ImportError: cannot import name 'current_scene_name' from 'ex45_extras' (D:\Programming\Python\LPTHW_Exs\ex45\ex45_extras.py)
Class names don't have a trailing () suffix — that's how you create an instance of one (i.e. by calling it).
Anyhow, if ex45_extras.py defines a class named A:
class A:
pass
Then you could import the class via a string containing its name, and then create an instance of it as shown below:
class Nav():
def __init__(self):
print("This is class Nav")
def play(self):
import ex45_extras
current_scene_name = 'A' # this is where the variable is defined
class_ = getattr(ex45_extras, current_scene_name) # Get class.
instance = class_() # Create instance of class.
print(f'{instance=}') # -> instance=<ex45_extras.A object at 0x010D4838>
x = Nav()
x.play()
guess it's because you're trying to import an string "A()" instead of a class A()
I am attempting to use a Directory to hold my growing list of frames and when my MAIN.py file is executed I want the code to load each frame into a Tkinter notebook widget.
I can do this manually by importing each file from the sub directory in my MAIN folder however I am attempting to have this load them pragmatically without having to know each file name and not having to import them myself.
I have made some progress but I got stuck at actually loading the class in the files. I can get a list of all files and I believe I am importing them just fine I just cannot get them to load as it keeps telling me that a module does not exist.
I must be misunderstanding something here as I cannot figure out how to properly call a class from the imported file.
Error:
C:\Users\USER\PycharmProjects\TEST\venv\Scripts\python.exe C:/Users/USER/PycharmProjects/TEST/MAIN/MAIN.py
# This below list is a result of my print statement to see if I got all the file names.
['TaskFrame', 'TestFrame1', 'TestFrame2', 'TestFrame3']
Traceback (most recent call last):
File "C:/Users/MCDOMI3/PycharmProjects/TEST/MAIN/MAIN.py", line 46, in <module>
ScriptManager().mainloop()
File "C:/Users/MCDOMI3/PycharmProjects/TEST/MAIN/MAIN.py", line 39, in __init__
self.add_frame_to_book(foo.tab_title)
AttributeError: module 'ScriptFrames.TaskFrame' has no attribute 'tab_title'
MAIN Code:
import tkinter as tk
import tkinter.ttk as ttk
from os.path import dirname, basename, isfile, join
import glob
import importlib.util
modules = glob.glob(join(dirname('.\ScriptFrames\\'), "*.py"))
__all__ = [basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]
print(__all__)
class ScriptManager(tk.Tk):
def __init__(self):
super().__init__()
self.book = ttk.Notebook(self)
self.book.grid(row=0, column=0, sticky='nsew')
for fn in __all__:
spec = importlib.util.spec_from_file_location('ScriptFrames.{}'.format(fn),
'.\ScriptFrames\\{}.py'.format(fn))
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
# Here is my problem I do not know if I am attempting to get the class
# attribute correctly here. I am sure that I am calling this wrong.
self.add_frame_to_book(foo.tab_title)
def add_frame_to_book(self, fname):
self.book.add(fname, text='test')
if __name__ == '__main__':
ScriptManager().mainloop()
Each test file is a simple tkinter frame.
Test Frame Code:
import tkinter as tk
import tkinter.ttk as ttk
class TabFrame(ttk.Frame):
def __init__(self):
super().__init__()
self.tab_title = 'Test Frame 1'
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
tk.Label(self, text='test').grid(row=0, column=0, sticky='nsew')
If it helps here is my file structure:
You're trying to access tab_title for the module, and not from the (Frame) class inside it.
Once the module is loaded, get its attributes using [Python 3.Docs]: Built-in Functions:
Get module attribute names (using dir)
For each name, get the corresponding attribute (using getattr)
Test whether the attribute is a ttk.Frame (as the module has other attributes as well)
If it is a frame class, instantiate it in order to access tab_title, otherwise just discard it
Example:
# ...
# The rest of your code
spec.loader.exec_module(foo)
# At this point, 'foo' module is loaded. Get its attributes
module_attrs = [getattr(foo, attr_name, None) for attr_name in dir(foo)]
# Filter out the attributes only keeping the ones that we care about
frame_classes = [attr for attr in module_attrs if isinstance(attr, (type,)) and issubclass(attr, (ttk.Frame,))]
# Then handle the module classes (it will probably be only one)
for frame_class in frame_classes:
if frame_class.__name__.startswith("Tab"): # Or any other filtering
# It would have been much more efficient to filter names when computing attr_names, but it's more consistent to be here (especially if other filtering beside name is needed)
frame_class_instance = frame_class() # Instantiate the class, as tab_title is an instance attribute
print(frame_class.__name__, frame_class_instance.tab_title)
Here is what I do.
def load(self, file=None):
file_type = path.splitext(file)[1].lstrip('.').lower()
if file_type == 'py' and path.exists(file):
spec = spec_from_file_location("module.name", file)
module = module_from_spec(spec)
spec.loader.exec_module(module)
self.nodes = module.config
I'm writing a graphic application that gives a word after I press a key in my electric piano using a database.
I'm using PyGame, Tkinter and Sqlite.
The application is pretty simple and is almost finished,
but I'm stuck with that error between my piano.py and the frontEnd.py.
The thing is that I want a Label that writes what was the last key I pressed and put it on a canvas.
I know the problem is related to the 'while True' and already changed it with 'while idKey < 176' but with this change I receive the "noneType" error.
This is the current code in my file piano.py
piano.py
import pygame
import pygame.midi
from pygame.locals import *
class backPiano():
def funcPiano(self):
self = backPiano
pygame.init()
pygame.fastevent.init()
event_get = pygame.fastevent.get
event_post = pygame.fastevent.post
pygame.midi.init()
input_id = pygame.midi.get_default_input_id()
i = pygame.midi.Input( input_id )
while True:
events = event_get()
if i.poll():
midi_events = i.read(10)
idKey = midi_events[0][0][0]
if idKey == 176:
return False
And the code in my frontEnd (only the function with the problem):
frontEnd.py
from tkinter import *
from tkinter import ttk, font
import multiprocessing
import time
import os
from database import dictionary, path
from piano import backPiano
class frontEnd(Frame):
def __init__(self, parent):
self.backPiano = backPiano()
def capturePiano(self):
backPiano.funcPiano(self)
superPiano = StringVar()
superPiano.set(backPiano.funcPiano(self).idKey)
labelPiano.configure(textvariable=superPiano)
self.parent.update()
canvasWidth = 500
canvasHeight = 500
w = Canvas(parent, width=canvasWidth, height=canvasHeight)
w.place(x=monitorWidth/2,y=monitorHeight/2, anchor=CENTER)
w.create_image(canvasWidth/2, canvasHeight/2, image=img, anchor=CENTER)
labelPiano = Label(parent)
labelPiano.place(x=monitorWidth/2,y=monitorHeight/2)
In the line 'superPiano.set(backPiano.funcPiano(self).idKey)' I tried:
"superPiano.set(backPiano.idKey)"
But because the variable is inside a function it can't be called with that.
The exact error I have is this:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\admin\AppData\Local\Programs\Python\Python37\lib\tkinter\__init__.py", line 1705, in __call__
return self.func(*args)
File "C:\Users\admin\Desktop\python\frontEnd.py", line 202, in <lambda>
command=lambda : capturePiano(self)).place(x=monitorWidth/9,y=monitorHeight/2,anchor=CENTER)
File "C:\Users\admin\Desktop\python\frontEnd.py", line 187, in capturePiano
superPiano.set(backPiano.funcPiano(self).idKey)
AttributeError: 'bool' object has no attribute 'idKey'
I can't upload all the code, but the error is in the While True but removing it destroys all my code because I need the loop.
Thank you very much (and sorry if I made grammar mistakes).
As the error message says: funcPiano is returning a boolean (True) so when you try to take the idKey it fails, because booleans don't have that.
I'm trying create a gui using Tkinter that grabs a username and password and connects to a remote server and does a function. I slapped together some messy code and it more or less worked, but when I tried to recreate it in a tidy module, it broke. Its probably a newbie python error, but I can't spot it. EDIT: to clarify, when it worked, the only class was setupGui and any methods were under that class. Now that I've separated the gui from the methods, its not working.
class setupGui(object):
def __init__(self, parent):
##omited general frame stuff
self.userIn = ttk.Entry(self.topFrame, width = 20)
self.userIn.grid(row = 1, column = 1)
self.passIn = ttk.Entry(self.topFrame, width = 20, show ="*")
self.passIn.grid(row = 2, column = 1)
#Buttons
self.setupbtn = ttk.Button(self.topFrame, text = "Start Setup", command = setup().startSetup())
self.setupbtn.grid(row = 3, column = 0, pady = 10)
class setup(object):
def__init__(self):
self.userName = setupGui.userIn.get()
self.userPass = setupGui.passIn.get()
def startSetup(self):
self.another_related_fucntion # about 4 related functions actually
if __name__ == '__main__':
root = Tk()
gui = setupGui(root)
root.mainloop()
And if I don't have the command attached to the button, everything works fine (but obviously does diddly squat except look pretty). And when I attached the command, I get this error:
Traceback (most recent call last):
File "macSetup.py", line 211, in <module>
gui = setupGui(root)
File "macSetup.py", line 45, in __init__
self.setupbtn = ttk.Button(self.topFrame, text = "Start Setup", command = setup().startSetup())
File "macSetup.py", line 69, in __init__
self.userName = setupGui.userIn.get()
AttributeError: type object 'setupGui' has no attribute 'userIn'
In your code, userIn is set up as an instance variable of setupGui objects, not as an attribute of the setupGui class itself.
The simplest solution would be to merge the setupGui and setup classes to move startSetup in as a method of setupGui, then use command=self.startSetup when you initialize setupbtn—this calls startSetup as a bound method, and self should thus refer to the setupGui object, which you can then use e.g. self.userIn.get() and self.passIn.get() on.
If you'd rather keep the logic you have in the setup class out of the setupGui class, you can separate it out like this:
class setup(object):
def __init__(self, username, userpass):
self.userName = username
self.userPass = userpass
def startSetup(self):
# as before
then add this method to the setupGui class:
def dosetup(self):
setup(self.userIn.get(), self.passIn.get()).startSetup()
and instantiate the Button with command=self.dosetup. (I would personally make the setup class a standalone function, but I don't know how complicated your startSetup routine actually is, so I assume you have a good reason for making it a class.)
The command attribute takes a reference to a function, but you're calling the function and giving the result to the command attribute. The net result is that you're calling the setup function at the time that you create the button, not at the time that you click the button. Things aren't fully initialized yet, so you get the error.
You're doing this:
self.setupbtn = ttk.Button(self.topFrame, text = "Start Setup", command = setup().startSetup())
... when you should be doing something like this:
self.setupbtn = ttk.Button(self.topFrame, text = "Start Setup", command = setup().startSetup)
Note the lack of the trailing () on startSetup.
If you don't want to instantiate setup until the button is clicked, you have a couple of choices. The best, arguably, is to create a method:
def _launch_setup(self):
setup().setupGui()
...
self.setupbtn = ttk.Button(..., command=self._launch_setup)
You could also use a lambda, but in this case I recommend a named method.
The class setupGui itself doesn't have the attribute userIn.
In the __init__ method of setupGui you give the attribute to the instance, not the class.