Creating an instance of a class existing in a different file(python) - python

I am trying to learn how to change my programs so that they use code from multiple python scripts. I have two scripts (these are big files, so cutting them down to only what's needed)
main.py
import pygame
import player #Imports the player.py script
p1 = hero("woody.png",[2,2]) #Creates an instance of hero
player.py
import pygame
import main
class hero:
def __init__(self,image,speed):
self.image = pygame.image.load(image)
self.speed = speed
self.pos = self.image.get_rect()
Running this gives me the following error:
AttributeError: 'module' object has no attribute 'hero'
I'm not quite understanding why it's trying to get an attribute instead of creating an instance. I've tried looking at other examples and how they are solving the problem but when I attempt to apply it to the code above it does not solve my issue.

To import hero from another module, you should write player.hero, or simply from player import hero.
Importing player in main and main in player would cause "circular references".
Here is the modified code:
main.py
import pygame
from player import hero # Imports the player.py script
p1 = hero("woody.png",[2,2]) # Creates an instance of hero
player.py
import pygame
class hero:
def __init__(self,image,speed):
self.image = pygame.image.load(image)
self.speed = speed
self.pos = self.image.get_rect()#.....etc

Like Athena said above, don't import main into player and player into main. This causes a import loop. Just simply import player into main
Secondly, you must say player.hero() if you want to use the class hero from the player module. Or if you want to just say hero(), you can say from player import*. This tells python to import all the files from player into the namespace main.
Be careful using this though, as a function or class from your player file may conflict with an already exiting function or class, with the same name.
And as a side note, classes in python general have their first letter capitalized.
here is how your files should look:
main.py
import pygame
import player #Imports the player.py script
p1 = hero("woody.png",[2,2]) #Creates an instance of hero
player.py
import pygame
class hero:
def __init__(self,image,speed):
self.image = pygame.image.load(image)
self.speed = speed
self.pos = self.image.get_rect()#.......etc

Drop the import main in player.py and change the last line in main.py to:
p1 = player.hero("woody.png",[2,2])
Edit:
Python does not know what class/function hero is. It needs you to tell it hero is a class in the player module. And that's what player.hero means.
Also never import one module from another and vice versa. You can get an import loop which is very hard to debug.
Lastly, in python it is common to name a class with a capital letter as Hero instead of hero.

Related

Why am I able to use the pygame module methods in one .py file even though I imported the module in another file?

I have two .py files. One of them is the main file with all the code and the other one is the file I decided to create to clean up my main file. I wanted to create a separate class in order to get the main file more organised, then simply import the class in the main file.
I was expecting that I'd have to import pygame in the additional file as well in order to use the methods it'd provide, such as .blit or .render. However, I noticed that PyCharm suggested me to get rid of the import, as it was redundant and never used. This is how the code in the additional file looks like. It uses the dis variable (dis = pygame.display.set_mode((DIS_WIDTH, DIS_HEIGHT)) ) that I'm passing in the main.py.
My own guess is that maybe the fact that I'm passing the dis variable accounts for this, but I'm not sure how it's done, since I'm new to OOP and to programming in general. I tried to look for the answer on the web, but I didn't find anything that would remind me my problem. So, I really hope there is an explanation for this kind of behavior.
from constants import * #constants is another additional file, containing some variables
class Scoreboard:
def __init__(self, m_display):
self.display = m_display
self.width = DIS_WIDTH
self.height = DIS_HEIGHT
def message(self, msg, color):
mesg = MAIN_FONT_STYLE.render(msg, True, color)
self.display.blit(mesg, [self.width / 6, self.height / 3])
def your_score(self, score):
value = SCORE_FONT_STYLE.render("Your Score: " + str(score), True, YELLOW)
self.display.blit(value, [0, 0])
constants.py contains import pygame in itself.
import pygame
pygame.init()
MAIN_FONT_STYLE = pygame.font.SysFont(None, 25)
SCORE_FONT_STYLE = pygame.font.SysFont(None, 35)
When you use from constants import * you import everything that is defined in the module constants, including anything it has imported itself. That can be a lot more than you bargained for, and can cause conflicts by defining names that you didn't expect. That's why it's not recommended practice. Instead, you can just qualify each symbol with the module name:
import constants
# ...
mesg = constants.MAIN_FONT_STYLE.render(msg, True, color)
Or you can import only the things that you need:
from constants import MAIN_FONT_STYLE, SCORE_FONT_STYLE
P.S. the suggestion is correct, you don't need to import the module to use the methods on the objects it creates.

Pylint Error with Python Turtle even though code executes properly

import turtle
class Polygon:
def __init__(self,sides,name,size=100,color='black',line_thickness=3):
self.sides=sides
self.name=name
self.size=size
self.color=color
self.line_thickness=line_thickness
self.interior_angles=(self.sides-2)*180
self.angle=self.interior_angles/self.sides
def draw(self):
turtle.color(self.color)
turtle.pensize(self.line_thickness)
for i in range(self.sides):
turtle.forward(self.size)
turtle.right(180-self.angle)
turtle.done()
square=Polygon(4,'Square')
square.draw()
Considering the code above, operating in VSCODE, I am wondering how to get rid of all the 'pylint' errors that continue to pop up which suggest something similar to the following:
Module 'turtle' has no 'color' member (pylint no-member)
Although the code executes just fine, it is unsettling to continue having to look at the error lines and I am wondering if there is a solution to this. Thanks for you time!
Rather than suppress the error message, why not fix the code? Turtle presents two APIs, a functional one and an object-oriented one. The functional one is derived from the object-oriented one at load time. Analysis tools can't look inside the source library file and see the functional signatures.
Since you're defining your own Polygon object, I don't see why you're not using the object-oriented interface to turtle. The import I use below blocks the functional interface and only allows access to the object-oriented one:
from turtle import Screen, Turtle
class Polygon:
def __init__(self, sides, name, size=100, color='black', line_thickness=3):
self.sides = sides
self.name = name
self.size = size
self.color = color
self.line_thickness = line_thickness
self.interior_angles = (self.sides - 2) * 180
self.angle = self.interior_angles / self.sides
def draw(self):
turtle.color(self.color)
turtle.pensize(self.line_thickness)
for _ in range(self.sides):
turtle.forward(self.size)
turtle.right(180 - self.angle)
screen = Screen()
turtle = Turtle()
square = Polygon(4, 'Square')
square.draw()
screen.exitonclick()
Note the subtle changes to the code to accommodate the object-oriented API. Now try your analysis of the code to see if this solves your problem.

Using instances from main module in other modules in Python

I am currently restructuring my code with modules and facing a problem regarding instances.
So far I created 3 scripts: main_script.py, launchgui.py, steppermotors.py.
main_script should only create instances to launch the GUI and initialize stepper motor configuration etc.
As I need the instance GUI of launchgui.LaunchGui also in a method of the class StepperMotor I thought I could just create the instance in the same way I did in the main script. I also need the instance of StepperMotors() to call the method "calculate_angle()" within the LaunchGui class.
Unfortunately, the GUI doesn't start anymore when creating the same instances in every module and I also don't receive any error messages.
Also I think this is not really best practice.
Therefore I wanted to ask if there is a "right" and working way to share instances with other modules that only have been created in the main script?
I hope you can help me with this!
See code snippets of the affected modules below:
main_script.py
import launchgui
import steppermotors
from tkinter import *
def main():
root = Tk()
gui = launchgui.LaunchGui(root) #Creates instance 'gui' of the class 'LaunchGui' from the module 'launchgui' & initialises class
stepper = steppermotors.StepperMotors() #Creates instance 'stepper' of the class 'StepperMotors' from the module 'steppermotors' & initialises class
root.mainloop() #Mainloop for tkinter-GUI
...
launchgui.py
#own modules
import steppermotors
#other modules
from tkinter import *
class LaunchGui:
def __init__(self, root):
''' This function creates the gui with all buttons etc.
when initialising this class
'''
#Instances
stepper = steppermotors.StepperMotors()
...(Gui buttons etc. here)
#Move to Position
btn_moveto_position = Button(control_frame, text='Move to Position', command=lambda:stepper.calculate_angle([0,0,0,0]))
...
steppermotors.py
#own modules
import launchgui
#other modules
from tkinter import *
class StepperMotors:
''' This class contains the configuration and control algorithms for
the stepper motors (Axis1-4)
'''
def __init__(self):
''' Setting Output-Pins, Microstep-Resolution, Transmission and
Ramping parameters for the different motors when
initialising the class
'''
#Instances of other classes
root = Tk()
gui = launchgui.LaunchGui(root) #Creates instance 'gui' of the class 'LaunchGui' from the module 'launchgui'
...(some other functions here)
def calculate_angle(self, previous_angles_list):
''' This function calculates the difference (=delta) between
the current and previous slider_values (=angle)
'''
#Current angles
current_angles_list = [] #Creates empty list to append current values
delta_angles_list = [] #Creates empty list to append difference between current and previous angles (=delta)
for idx in range(self.number_of_motors): #Looping through list indices (idx)
current_angles_list.append(gui.SLIDER_list[idx].get()) #Appends currently selected slidervalues (=current angle) to current_angles_list
deltas_list.append(int(current_angles_list[idx] -
previous_angles_list[idx])) #Calculates the difference between current and previous angle as an integer and appends it to delta list

"AttributeError: 'Turtle' object has no attribute 'colormode'" despite Turtle.py having colormode atttribute

I tried running the code that uses the Turtle library on this site, shown here,
import turtle
import random
def main():
tList = []
head = 0
numTurtles = 10
wn = turtle.Screen()
wn.setup(500,500)
for i in range(numTurtles):
nt = turtle.Turtle() # Make a new turtle, initialize values
nt.setheading(head)
nt.pensize(2)
nt.color(random.randrange(256),random.randrange(256),random.randrange(256))
nt.speed(10)
wn.tracer(30,0)
tList.append(nt) # Add the new turtle to the list
head = head + 360/numTurtles
for i in range(100):
moveTurtles(tList,15,i)
w = tList[0]
w.up()
w.goto(0,40)
w.write("How to Think Like a ",True,"center","40pt Bold")
w.goto(0,-35)
w.write("Computer Scientist",True,"center","40pt Bold")
def moveTurtles(turtleList,dist,angle):
for turtle in turtleList: # Make every turtle on the list do the same actions.
turtle.forward(dist)
turtle.right(angle)
main()
in my own Python editor and I got this error:
turtle.TurtleGraphicsError: bad color sequence: (236, 197, 141)
Then, based on this answer on another site, I added in this line before "nt.color(......)"
nt.colormode(255)
Now it's showing me this error
AttributeError: 'Turtle' object has no attribute 'colormode'
Okay, so I checked my Python library and looked into the contents of Turtle.py. The colormode() attribute is definitely there. What is making the code able to run on the original site but not on my own computer?
The issue is that your Turtle object (nt) doesn't have a colormode method. There is one in the turtle module itself though.
So you just need:
turtle.colormode(255)
instead of
nt.colormode(255)
Edit: To try to clarify your question in the comment, suppose I create a module called test.py, with a function, and a class, 'Test':
# module test.py
def colormode():
print("called colormode() function in module test")
class Test
def __init__(self):
pass
Now, I use this module:
import test
nt = test.Test() # created an instance of this class (like `turtle.Turtle()`)
# nt.colormode() # won't work, since `colormode` isn't a method in the `Test` class
test.colormode() # works, since `colormode` is defined directly in the `test` module
Screen class in turtle module has colormode() method.
You can call screen_object.colormode(255).
In your code it would be:
wn.colormode(255)
The problem is that you need to set up the colormode() attribute = 255. The class to reference is Screen(), based on your code you referenced this code as wn = turtle.Screen(). In order for you to make your code work, just by adding the following line of code.
wn.colormode(255)

Python and Pygame: Avoiding creating display surface twice

Heyo, this is a bit of an extension of the "Imports within imports" question asked earlier by me, so moderators feel free to merge the 2.
I have 2 files: A.py and B.py
#A.py
import pygame
import B
pygame.init()
tv = pygame.display.set_mode((256, 256))
tv.blit(<some surface here>)
#B.py
import pygame
pygame.init()
tv.blit()??? <--- I need to blit to tv, but how do I do it here?
I've tried making a blank file called Globe and assigning global values to it, but most of the time I've found it just makes my code look clunky and hard to write.
As well.. I don't want to init pygame twice either.
Is there any 'Pythonic' way to do it?
This question could really apply to any structured python application.
An executable python script is going to have an entry-point. This is the script that you call to start the application. Within this script, it can import library modules to reuse extended functionality.
Your application needs to have a single entry point. Lets assume it will be A.py.
B.py would be a library module that you will import and use its functions. It should not have to expect a global tv variable to operate on. Instead, it should have, at the very least, functions that take arguments. Or even, a class that is instantiated with a surface to use. The benefit of this approach is that your B module is now reusable and not dependent on some executable main script providing a global surface always called tv
B.py
def blitSpecial(surf):
surf.blit()
A.py
import B
tv = pygame.display.set_mode((256, 256))
B.blitSpecial(tv)
This is a good habit to get into. If all your modules depend on global objects from a main script, they will be far less reusable.
Specifically for your pygame situation, everything with the screen surface should be happening in A.py which is using all of the other modules for custom classes and utility functions.
You can write functions that take pygame.Surface objects as parameters:
class TV():
def __init__(self):
self.img = ...
### insert code to load image here
self.rect = self.img.get_rect()
def draw(self, surface):
surface.blit(self.img, self.rect.topleft)
def erase(self, surface, background):
surface.blit(background, self.rect)
I don't personally know how fast/slow this is compared to other sprite-based engines, but it's a very quick way to build out a class that can draw/erase itself.
To use it, just create a display screen and a TV object.
screen = pygame.display.set_mode((256, 256))
background = pygame.Surface((0,0),(256,256))
background.fill(pygame.Color(0,0,0))
screen.fill(pygame.Color(0,0,0))
myTVobj = TV()
Every time you want to draw a copy of the TV onto the screen you call
myTVobj.draw(screen)
To erase the object, use
myTVobj.erase(screen, background)
Then you can do fun stuff later with objects created from the TV class, like stick them in a list.
tv_list = []
tv_list.append(myTVobj)
You can add a whole bunch of TVs to a list and draw all of them at the same time.
tv_list = []
tv_list.append(myTVobj)
tv_list.append(myTVobj)
tv_list.append(myTVobj)
for tv in tv_list:
tv.draw(screen)
Or you can erase them all just by changing one line
for tv in tv_list:
tv.erase(screen)
Finally, you can add one more function to your TV class that lets you move it around. If you treat the .rect member as a 'position marker', all you have to do is fiddle with its members (hehe) to change your object's onscreen update location.
def move(self, move_amount=(1,0):
self.rect.move_ip(move_amount[0], move_amount[1])
You only need to call pygame.init() once, so I think your code should look something like this:
#A.py
import pygame
import B
def setup():
pygame.init()
tv = pygame.display.set_mode((256, 256))
...
mysurface = ...
tv.blit(mysurface)
return tv
#B.py
import pygame
def mydraw(surface):
...
surface.blit()
# In whatever file you like :)
if __name__ == '__main__':
surface_A = B.setup() # Do this first
mydraw(surface_A)

Categories