Understanding import and scope - python

I am a noob to python and I am trying to understand how the import statement works.
I am running python 3.6.
I have IDLE working.
My test directory is setup like
directory:test_py
In the directory test_py I have files
test_calc.py,button.py,graphics.py
graphics.py is a graphics library.
My main program is in test_calc.py.
In test_calc.py I use from graphics import *. Its the first line
button.py is a module that contains a class definition, Button.
It also uses the graphics library graphics.py.
The main program in test_calc.py uses the Button class.
The Button class definition also calls Point, a function from the graphics library.
I thought the call to create a Button object would be able to use the graphcs
library from the statement from graphics import* in test_calc.py, but it cant.
button.py also needs a from graphics import * to access the graphics library functions.
Is there a way I can have just the **from graphics import* ** in test_calc.py
and have the Button class also see the graphics library
module and objects without have to also import it in button.py?
I have spent a easy 8hrs reading and trying to figure this out.
Any help is appreciated.
here is the complete test_calc.py file
from graphics import *
from button import *
def __createButtons(win):
#create a list of buttons
bSpecs = [(2,1,'0'), (3,1,'.'),
(1,2,'1'), (2,2,'2'), (3,2,'3'), (4,2,'+'), (5,2,'-'),
(1,3,'4'), (2,3,'5'), (3,3,'6'), (4,3,'*'), (5,3,'/'),
(1,4,'7'), (2,4,'8'), (3,4,'9'), (4,4,'<-'), (5,4,'C')]
buttons = []
for (cx,cy,label) in bSpecs:
buttons.append(Button(win, Point(cx,cy), .75,.75,label))
###create larger = button
buttons.append(Button(win, Point(4.5,1), 1.75, .75, "="))
#activate all buttons
for b in buttons:
b.activate()
def main():
#create the window for the calculator
win = GraphWin("Calculator")
win.setCoords(0,0,6,7)
win.setBackground("slategray")
win= win
__createButtons(win)
main()
here is the beginning of button.py
The Point function from graphics library is not available to Button without this statement
from graphics import *
class Button:
def __init__(self, win, center, width, height, label):
w,h = width/2.0, height/2.0
x,y = center.getX(), center.getY()
self.xmax, self.xmin = x+w, x-w
self.ymax, self.ymin = y+h, y-h
#Point function from graphics library
p1 = Point(self.xmin,self.ymin)
p2 = Point(self.xmax, self.ymax)

Short answer: No, there is no way to do that.
graphics.py is out of scope of button.py, because test_calc.py imported graphics and test_calc.py is in scope of button.py.
However, if you really want to save an extra line, you can just remove from graphics import * from test_calc.py because button.py already imported graphics.py.

No, you can't do this. If this worked the way you want, you'd break namespacing and scope implicitly within button, not just explicitly in test_calc.
The scope of a module (used by all things defined in that module) is defined by what it imports, and what it defines (and in poor design cases, by things monkey=patched in later). Adding in things from "stuff defined in the things that imported me" is a terrible idea.
The correct solution here is to just do:
# If a package, explicit relative imports avoid checking the whole of sys.path
from .graphics import *
# IF not a package, use plain names without leading .s
from graphics import *
at the top of your button.py file, so it actually specifies where it expects to get the APIs from (ideally, you explicitly list the names to use, not just *, which helps static checking code determine if all your names are defined, helps maintainers figure out where specific names came from, and avoids the risk of two modules providing the same name and stomping all over each other). Since you've already done this, you're golden; you can just omit the import of graphics stuff in test_calc.py (assuming test_calc.py itself doesn't use it).
If, for some insane reason, this really is necessary, the simplest way to do it is to bypass import semantics and explicitly eval the code for button in the same scope that includes the graphics stuff. In test_calc.py, after doing from graphics import *, you'd explicitly open and read the contents of button.py (into, for example, button_source) then do:
eval(compile(button_source, button_path, 'exec'), globals())
which would compile and evaluate the source code of button.py using test_calc's global scope (which includes graphics stuff now), so the newly defined stuff would be defined in test_calc's globals, and see all the stuff defined there as its "native scope" too.

Related

How to call modules from another .py file

first time posting!
I'm fairly new to python (and programing!), and I have started to make a basic tkinter program. The code is getting pretty long now, and I want to split it between different .py files to make it more navigatable. So far, all my code exists in classes, seperating the main window, from calculation functions, secondary windows and so on.
First question, is it considered good practice to split code like this? I feel like it is, but want to make sure!
Secondly, how is the best way to handle modules between files?
For example, I have tkinter and matplotlib imported in the main_window.py file. I have the main_window class function which calls a different class which I want to move to another file, but this secondary class has a line which calls tkinter. I want to pass self through the secondary function so that it is using the same instance, as it were.
Here is an example code to illustrate. The first file, main_window.py:
# main_window.py
import tkinter as tk
import matplotlib
import matplotlib.pyplot as plt
import app_design # app_design.py contains the code I want to break out
class MainWindow:
def __intit__(self, root):
self.root = root
self.start_gui()
def start_gui(self):
# a bunch of code
...
# in reality this is read from a program file on startup
color_mode = "Dark"
# This is just an example, the matplotlib call in the other app is in the __init__
self.bg_col_mode = tk.StringVar()
self.bg_col_mode.set(app_design.AppColors(color_mode).background())
# a bucnh more code of various tk widgets and matplotlib charts
...
if __name__ == '__main__':
app = MainWindow(tk.Tk())
app.root.mainloop()
and then an example of some code which I'd like to split out. This is not the only instance of the class referencing a module outside the MainWindow class, but it works as an example:
# app_design.py
class AppColors:
def __init__(self, color_mode):
self.color_mode = color_mode
if self.col_mode == "Dark":
self.plt.style.use("Dark") # it is this .plt call that has moved from the main_window.py file to the new file
else:
self.plt.style.use("Light")
def background_mode(self):
if self.color_mode == "Dark":
return "#292929" # colour code
else:
return "#F8F1F1"
Hopefully this makes sense!
First question, is it considered good practice to split code like this? I feel like it is, but want to make sure!
I actually don't know myself, I only have coded stuff for backend.
Secondly, how is the best way to handle modules between files?
You simply import the file (or function directly).
Example:
file1.py
def hello(name):
print("Hello ", name)
file2.py
from file1 import hello
hello("arjix")
this way you can directly use the function hello
or
import .file1
file1.hello("arjix")
PS: Make sure these two files are in the same folder.

How to change global variable using other file by importing

I have made this calculator on Python with GUI using Tkinter, it's my university project but there are some requirements and one is that every function should be imported in the main GUI file not to be placed there.
For example, I have created a null operator textinput for textvariable and buttons from 1 to 9 and defined a button click handler function for it:
def btnClick(numbers):
global operator
operator=operator+str(numbers)
text_Input.set(operator)
And I have saved this in the other file called "Buttons.py"
Now when i call it in my gui file like this and make a button to execute it
import Buttons
button7=Button(cal,padx=16,bd=4,fg='black',font=('The Citadels',20),
text='7',command=lambda:Buttons.btnClick(7),bg='ghost white')
.grid(row=5,column=0)
But get an error saying
name 'operator' is not defined
What is missing for my code to work ?
When you use the import statement on its own, you import the module but you don't import the namespace. operators is not defined because it is part of the module's namespace. To fix this, you can either use Buttons.operator instead of operator, or you can change your import statement to look like from Buttons import *, which will import all the variables from Buttons into your namespace as well.

winsound in a tkinter variable

I have made a hangman game in Tkinter with buttons for each letter, each button is linked to a function that will process the letter and tell me if it is correct or not. How can I make a function a correct sound and an incorrect sound, I do not want to say where I am getting my wav file from, because it will move (it will always be in the same file as my programme).i do not want to download another module and the answers from another question do not work.
i tried this but it did not make a sound:
def no():
lambda: PlaySound('wrong.wav', SND_FILENAME)
def yes():
lambda: PlaySound('right.wav', SND_FILENAME)
You seem unclear what lambda does:
Python supports the creation of anonymous functions (i.e. functions that are not bound to a name) at runtime, using a construct called "lambda".
In the example you have given you have not created any anonymous function so therefore they are totally unnecessary. Just remove them and everything should work.
def no():
PlaySound('wrong.wav', SND_FILENAME)
def yes():
PlaySound('right.wav', SND_FILENAME)
Remember however winsound is Windows only (so no cross platform development) and to use these functions you need from winsound import *

How to update any log or graph in output window during processing?

I'd like to make auto test tool with Python. One thing I'd like to achieve is updating Test log in log window. However, all logs are displayed after finishing Test process. How can I show log during process?
In addtion, during processing, GUI stops working with error message"Not Responding". How to resolve it? thanks for your help in advance.
(Window 7, 64bit, Python 3.4, tkinter, matplotlib)
==========================================================================
from tkinter import *
from matplotlib import pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from time import sleep
root= Tk()
root.title("Auto Test")
root.geometry('300x700')
root.configure(background='white')
datac1_frame = Frame(root,bg="white",width=400, height=30)
datac1_frame.pack(side=TOP, anchor = W,padx= 5,fill=BOTH)
data1_frame = Frame(root,bg="white",width=400, height=280)
data1_frame.pack(side=TOP, anchor = W,padx= 5,fill=BOTH)
log_frame = Frame(root,bg="blue",width=400, height=80)
log_frame.pack(side=TOP, anchor = N,padx= 5,fill = Y)
#Output Box
logmes = Text(log_frame,width=800)
logmes.pack()
class autotest:
def DUT_config(self):
sleep(1)
logmessage('DUT configuration')
def measurement(self):
sleep (2)
logmessage('DUT is under measurement')
def freqentry(frame,text, command = None):
label = Label(frame,text='Channel', bg = 'chartreuse',
width= 10).pack(side = LEFT, padx=10, pady = 2)
entry = Entry(frame, width = 18, text = text,
justify = CENTER).pack(side = LEFT)
button = Button(frame, text = "Run", bg = "lightsteelblue",
width = 5,padx=4, command = command).pack(side = LEFT)
def clickrun():
logmessage ('button is clicked')
for i in range(5):
logmessage('# of try', i)
at.DUT_config()
sleep(1)
at.measurement()
def logmessage(*args):
logmes.insert(END, args)
logmes.insert(END, '\n')
logmes.see(END)
#Channel, freq, Run button alignment
freqentry(datac1_frame, '2000MHz', clickrun)
## Data Gathering
f_0 = Figure(figsize=(2.5,2.5), dpi =100)
a_0 = f_0.add_subplot(111)
a_0.xaxis.grid(True)
a_0.yaxis.grid(True)
a_0.plot ()
## Display on the plot
canvas = FigureCanvasTkAgg(f_0, data1_frame)
canvas.show()
canvas.get_tk_widget().pack(side=LEFT)
at = autotest()
plt.show()
root.mainloop()
Hi there and welcome to StackOverflow.
First issue I see is your handling of scope. I suppose you posted this as a Minimal, Complete, and Verifiable example.
If this is your complete code, please be advised that there are some issues you should resolve. Your function def logmessage(*args) uses the variable logmes which is defined outside your function and additionaly used in other functions as well.
Like in most programming languages, the scope of a function limits access of variables in python. If you want to use the logmes listbox "globally" declared, you need to tell that to the functions as well by declaring the variable global inside the function (sample below)
# We are inside the main routine here - global scope
root= Tk()
log_frame = Frame(root,bg="blue",width=400, height=80)
log_frame.pack(side=TOP, anchor = N,padx= 5,fill = Y)
#Output Box
logmes = Text(log_frame,width=800)
logmes.pack()
# Here come functions having their own scope
def logmessage(*args):
# Use variables from global scope - global declaration
#################################
global logmes
#################################
logmes.insert(END, args)
logmes.insert(END, '\n')
logmes.see(END)
Using global variables is often seen as some kind of a bad habit, as it reduces the ways of reusing your code in other purposes. I personally prefer using classes in modules and putting everything that belongs together inside a class. This results in a module structure like
Module Header: e.g. commentary lines, version, author, shebang, etc.
Import Section: here i import whatever i need
Class and Function Section: here i define my classes and functions
Main Routine: starting with if __name__=="__main__": I start processing if the script is called and not imported, function calls happen only here
Unfortunately I see a second issue as well - inside your "Import Section" you are importing from matplotlib import pyplot as plt. Later on in your main routine you are calling plt.show. I am not using matplotlib often, but if I remember it correctly, you first define a pyplot Object and then call the show routine on the object, not the Module itself.
Do you receive any error messages inside your python console output? (Supposing you are calling the script from the commandline) or do you call it by double-clicking it? How do you store the file? I am focusing on the file-extension here, as it does make a difference if you store it as *.pyw or *.py in having or not having a console output window.
Another issue I just recognized is your class definition. As the documentation of the class module in python states out, a class is sth. that supports
two kinds of operations: attribute references and instantiation.
You are using your class as sth. like a function wrapper. You are not using inheritance nor are you declaring an __init__ funcionality to instantiate class variables.
It is no question that python gives you the ability to do so, but please keep in mind that your use of a class does more do sth. a module would do. Declare a namespace for certain functions that do not need more than the parameters passed (if any).
I do really recommend you have a very close look at the class documentation. This really helps improving your code and reduces the necessity to be slowed down by asking questions and waiting for replys on stuff the documentation can tell you directly and using simple examples.
Please do not see my last comment as any offense as this is definitely not intended here, just as a recommendation to help you improve in your work.

Seems like python is partial

The following is a function I created, and put it in a file called last_function.py
from tkinter import*
def new_gui(app,sound_file,mixer):
track=mixer.Sound(sound_file)
def track_toggle():
if ballCheckbutton.get()==1:
track.play(loops=-1)
else:
track.stop()
ballCheckbutton=IntVar()
c1=Checkbutton(app,text="check me out",command=track_toggle,variable=ballCheckbutton)
c1.pack(side=LEFT)
ballScale=DoubleVar()
def ScaleVolume(v):
track.set_volume(ballScale.get())
ballScale.set(track.get_volume())
s1=Scale(app,variable=ballScale,resolution=0.1,command=ScaleVolume,orient=HORIZONTAL,from_=0.0,to=1.0,label="volume")
s1.pack()
and this is the file i use.. to call the code and run it..
from tkinter import *
import pygame.mixer
from last_function import*
app=Tk()
mixer=pygame.mixer
mixer.init()
new_gui(app,"49119_M_RED_HardBouncer.wav",mixer)
def close():
mixer.stop()
app.destroy()
app.protocol("WM_DELETE_WINDOW",close)
app.mainloop()
Everything works fine.. but my query is...
1> Why can't I remove from tkinter import* from the last_function file.. cause anyway it's got that on the top of the file that's calling it right. Why do I get an error saying IntVar() not defined.
2> Why do I have to pass mixer as a parameter in the function? can the function not inherit it directly from import pygame.mixerthat's on top of the file calling it?
What I mean to say is. THERE ARE TKINTER COMPONENTS ALSO BEING USED, BUT I DON'T PASS TKINTER AS A PARAMETER.. Do I ! then why is there this... selective parameter assignment??
I'm really confused!!!
1> Why can't i remove from tkinter
import* from the last_function file..
cause anyway it's got that on the top
of the file that's calling it
right.Why do i get an error saying
IntVar() not defined
The Python "import" follows the same scoping rules as the rest of the Python language. By "import" at the top of your second files does not make the Tkinter namespace available to the last_function.py module. Tkinter also needs to be imported there.
2>why do i have to pass mixer as a
parameter in the function? can the
function not inherit it directly from
import pygame.mixerthat's on top of
the file calling it? WHAT I MEAN TO
SAY IS. THERE ARE TKINTER COMPONENTS
ALSO BEING USED,BUT I DON'T PASS
TKINTER AS A PARAMETER.. DO I!! then
why is there this.. selective
parameter assignment??
With the way you have this coded, you need to pass mixer because you are modifying it in your second file with:
mixer.init()
If you reimported mixer in your last_function.py, you would be getting another instance of mixer and not the one previously imported. There is nothing selective about this since both of your files have the Tkinter namespace imported.
You should try and re-factor this code to avoid having to import Tkinter into two modules and having to init mixer in one module and pass it to another.

Categories

Resources