Global variable not filled in python3 ttk - python

I am trying to use a ttk.notebook in python3 on a Linux (Ubuntu 14.04LTS) system to read Blockquote`# -- coding: utf-8 --data from experiments and perform several curve fits on this data in order to calculate the coefficients needed for a Finite Element calculation.
The actual calculation of these curves I have already performed without tkinter. I am now setting up the user interface.
All works well, until I want to fill in the variable a as global variable, noting is exported to the Ipython shell (using spyder2). The variables: CreepData and path2 which are made global in the same way are visble in the Ipython shell.
I am not sure if the way I am extracting values from the combo boxes or the Entry fields on the notebook page select units and run work in the intended way.
See the code below:
""" Created on Sat Jan 16 18:56:16 2016
#author: peterk
Derived frpm a lot of posted examples """ import csv from copy import
deepcopy import numpy as np import matplotlib import scipy import
matplotlib matplotlib.use("TkAgg") from
matplotlib.backends.backend_tkagg import
FigureCanvasTkAgg,NavigationToolbar2TkAgg from matplotlib.figure
import Figure from matplotlib import pyplot as plt from scipy import
stats from scipy.optimize import curve_fit from tkinter import *
import tkinter as tk import tkinter.font as tkFont import tkinter.ttk
as ttk from tkinter import filedialog path2="./creep.csv"
class CreepNotebook(ttk.Frame):
def __init__(self, isapp=True, name='notebookdemo'):
ttk.Frame.__init__(self, name=name)
self.pack(expand=True, fill="both")
self.master.title('Creep fit')
self.isapp = isapp
self._create_widgets()
self.master.minsize(1920,1000)
def _create_widgets(self):
self._create_main_panel()
def _create_main_panel(self):
mainPanel = ttk.Frame(self, name='demo')
mainPanel.pack( expand=True, side="top", fill="both")
# create the notebook
nb = ttk.Notebook(mainPanel, name='notebook')
# extend bindings to top level window allowing
# CTRL+TAB - cycles thru tabs
# SHIFT+CTRL+TAB - previous tab
# ALT+K - select tab using mnemonic (K = underlined letter)
nb.enable_traversal()
nb.pack(fill="both", padx=2, pady=3,expand=True)
self._create_readme_tab(nb)
self._create_import_data_tab(nb)
self._create_select_units_run_tab(nb)
self._create_text_tab(nb)
def _create_readme_tab(self, nb):
# frame explaining the app
frame = ttk.Frame(nb, name='readMeFirst')
# widgets to be displayed on 'Description' tab
msg = ["Ttk is the new Tk themed widget set. One of the widgets ",
"it includes is the notebook widget, which provides a set ",
"of tabs that allow the selection of a group of panels, ",
"each with distinct content. They are a feature of many ",
"modern user interfaces. Not only can the tabs be selected ",
"with the mouse, but they can also be switched between ",
"using Ctrl+Tab when the notebook page heading itself is ",
"selected. Note that the second tab is disabled, and cannot "
"be selected."
" aaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"ccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"dddddd",
"eeeee",
"f",
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
" here is text too"]
lbl = ttk.Label(frame, wraplength='4i', justify=tk.LEFT, anchor=tk.CENTER,
text=''.join(msg))
neatVar = tk.StringVar()
btn = ttk.Button(frame, text='Neat!', underline=0,
command=lambda v=neatVar: self._say_neat(v))
neat = ttk.Label(frame, textvariable=neatVar, name='neat')
# position and set resize behaviour
lbl.grid(row=0, column=0, columnspan=2, sticky='new', pady=5)
btn.grid(row=1, column=0, pady=(2,4))
neat.grid(row=1, column=1, pady=(2,4))
frame.rowconfigure(1, weight=1)
frame.columnconfigure((0,1), weight=1, uniform=1)
# bind for button short-cut key
# (must be bound to topplevel window)
self.winfo_toplevel().bind('<Alt-n>', lambda e, v=neatVar: self._say_neat(v))
# add to notebook (underline = index for short-cut character)
nb.add(frame, text='ReadMeFirst', underline=0, padding=2)
def _say_neat(self, v):
v.set('Yeah, I know...')
self.update()
self.after(500, v.set(''))
# return path2
# =============================================================================
def _create_import_data_tab(self, nb):
# Populate the second pane.
frame = ttk.Frame(nb, name="import data")
global l
global k
global sigma
global creepData
global filen
global path2
butn=ttk.Button(frame, text='select csv file', command=self.askopenfilename2)
butn.pack(side="top")
self.file_opt = options = {}
options['defaultextension'] = '.csv'
options['filetypes'] =[('csv files', '.csv'),('all files', '.*')]
options['initialdir'] = '.'
options['initialfile'] = 'creep2.csv'
options['parent'] = nb
options['title'] = 'Select csv file'
global creepData
print("path2 in _create_import_data_tab")
print (path2)
nb.add(frame, text='Import data', underline=0)
def askopenfilename2(self):
global path2
global creepData
path2 = filedialog.askopenfilename(**self.file_opt)
print("path2 in askopenfilename2")
print(path2)
creepReader=csv.reader(open(path2, newline=""), delimiter=',')
creepData=list(creepReader)
#enter code here
========================================
def _create_text_tab(self, nb):
# populate the third frame with a text widget
frame = ttk.Frame(nb)
txt = tk.Text(frame, width=40, height=10)
vscroll = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=txt.yview)
txt['yscroll'] = vscroll.set
vscroll.pack(side=tk.RIGHT)
# txt.pack(tk.fill=BOTH, tk.expand=True)
txt.pack() # btn2l.pack(side="top", pady=5)
# btn2.pack(side="top", pady=2)
# w.pack(side="top", pady=2)
# neatVar = tk.StringVar()
# btn = ttk.Button(frame, text='Neat!', underline=0,
# command=lambda v=neatVar: self._say_neat(v))
# neat = ttk.Label(frame, textvariable=neatVar, name='neat')
# def _say_neat(self, v):
# v.set('Yeah, I know...')
# self.update()
# self.after(500, v.set(''))
# add to notebook (underline = index for short-cut character)
nb.add(frame, text='Text Editor', underline=0)
#============================================================
def _create_select_units_run_tab(self, nb):
# select units and perform the calculation
frame = ttk.Frame(nb, name="select units and calculate")
global l
global k
global sigma
global creepData
global a
a=tk.StringVar()
frame.grid(column=0, row=0, rowspan=12, columnspan=5,sticky=(N,S,E,W))
units = ('m,kg,s', 'mm,Mg,s', 'mm,kg,ms')
a=tk.StringVar()
cbl0 = ttk.Label(frame, text='Select or fill in the required fields and push the run button')
cbl1 = ttk.Label(frame, text='Select units used in your FE-prgram')
cb1 = ttk.Combobox(frame, value=units, state='readonly')
# cbl1.pack(side="top")
# cb1.pack(side="top")
time_units=('hrs', 's','ms')
cbl2=ttk.Label(frame, text='Select time units used in the csv file')
cb2 = ttk.Combobox(frame, value=time_units, state='readonly')
# cbl2.pack(side="top")
# cb2.pack(side="top")
strain_units=('strain [%]', 'strain [-]','CreepModulus [MPa]')
cbl3=ttk.Label(frame, text='Select strain or modulus units used in the csv file')
cb3 = ttk.Combobox(frame, value=strain_units, state='readonly')
# cbl3.pack(side="top")
# cb3.pack(side="top")
yml=ttk.Label(frame, text=' Input Anisotropic Youngs Modulus in MPa')
ym=Entry(frame)
# yml.pack(side="top")
# ym.pack(side="top")
isfl=ttk.Label(frame, text='Input Isotropic factor')
isf=Entry(frame)
# isfl.pack(side="top")
# isf.pack(side="top")
run1Var = tk.StringVar()
btn2 = ttk.Button(frame, text='Run', underline=0,
command=lambda w=run1Var: self._run1(w))
btn2l = ttk.Label(frame, textvariable=run1Var, name='run1')
cbl0.grid(column=0, row=0, sticky=W, pady=100)
cbl1.grid(column=6, row=1, sticky=W, pady=25)
cb1.grid(column=6, row=2, sticky=W, pady=2)
cbl2.grid(column=6, row=3, sticky=W, pady=25)
cb2.grid(column=6, row=4, sticky=W, pady=2)
cbl3.grid(column=6, row=5, sticky=W, pady=25)
cb3.grid(column=6, row=6, sticky=W, pady=2)
yml.grid(column=6, row=7, sticky=W, pady=25)
ym.grid(column=6, row=8, sticky=W ,pady=2)
isfl.grid(column=6, row=9, sticky=W, pady=25)
isf.grid(column=6, row=10, sticky=W, pady=2)
btn2l.grid(column=6, row=11, sticky=W, pady=25)
btn2.grid(column=6, row=12, sticky=W, pady=2)
nb.add(frame, text='Select data and run', underline=0, padding=2)
a=cb1.get()
print(a)
print(cb1.get())
yms=ym.get()
isfs=isf.get()
def _run1(self, w):
# global CreepData
# global creepDat
# creepDat=deepcopy(creepData)
w.set('CreepData is copied')
self.update()
self.after(500, w.set(''))
#===================================================================
if __name__ == '__main__':
CreepNotebook().mainloop()`
If needed I can upload the csv.files on my repro, but I do not think it is needed for answering the question. The run1 function would be used to fit the data curves and return a message that the calculations were performed.

CHANGED - sorry I did not initially understand your question, prehaps this is more helpful:
first I'd recommend taking a look at both of these:
when to use global statement
Share variables between methods
second, you are creating the input entries in _create_select_units_run_tab and then you do:
def _create_select_units_run_tab:
...
a = cb1.get()
...
this will get the contents immediately after being created and even before .mainloop() is called so it will absolutely always be an empty string, in order to get the content later in _run1 you need to keep track of the entries instead:
def _create_select_units_run_tab:
...
self.input_fields = [cb1,cb2,cb3,ym,isf]
#this will give all other methods access to the input fields
#then to get them back you can use this:
def get_inputs(self):
return [entry.get() for entry in self.input_fields]
#then in run you can get the data in the fields
def _run1(self, w):
inp_data = self.get_inputs()
print(inp_data)
if "" in inp_data:
w.set("you need to fill everthing in first!")
else:
w.set(str(inp_data))
...
While testing the code I cancelled the file dialog window which threw an error because path2 == "" so you may want a check:
def askopenfilename2(self):
...
if path2 is not "":
creepReader=csv.reader(open(path2, newline=""), delimiter=',')
creepData = list(creepReader)
#else:
# creepData = None # or just leave it as the last used value?
...
not to mention the open() is never closed (Input and Output Python documentation):
It is good practice to use the with keyword when dealing with file objects. This has the advantage that the file is properly closed after its suite finishes, even if an exception is raised on the way. It is also much shorter than writing equivalent `try-finally` blocks:
with open('workfile', 'r') as f:
... read_data = f.read()
>>> f.closed
True
So you may want to implement that too:
with open(path2, newline="") as file_obj:
creepReader=csv.reader(file_obj, delimiter=',')
Lastly I'd like to draw attention to:
self.after(500,w.set('')
w.set('') is being called immediately and using the return value None in self.after, to instead call w.set with the argument you need to do this:
self.after(500,w.set,'')
then tkinter will call it after 500ms forwarding any extra arguments it receives in after
There may be other issues with your code that I missed but I hope this helped.

Related

How can I "reset" the size of a Window on Tkinter when using Notebook?

I know this has been asked before but none of the solutions that I've seen so far seem to work.
So here's the exact case: I'm building an app that has 3 different tabs (using ttk.Notebook).
The initial contents of the 3 tabs is different: the second is the smallest and the third is he biggest.
Now here comes the issue (I'm using grid, btw): when I change from the first to the second, there is an extra empty space (because the content of the first Tab is a little bigger than the one of the second, and the same happens when I go from the third to the second/first, so after using the third tab and changing to another one, the size stays as if I still was in the third.
I add some of the code that I'm using, but since I'm not sure what is going wrong, I'll let you ask for whatever extra code you might need to help me find the solution.
# Tk modules
import tkinter as tk
from tkinter import ttk
# # define main()
def main():
app = App()
MainWindow(app)
app.mainloop()
# Frame for data input
class RegisterChooseType(ttk.Frame):
def __init__(self, container):
# Inheritance
super().__init__(container)
self.container = container
# Var for selection
self.selected_value = tk.IntVar()
# Options
self.options = {"padx": 10, "pady": 10}
# Doc types
self.doc_types = ("Incoming Bill", "Outgoing Bill", "Contract", "Other")
# Labels
self.barcode_label = ttk.Label(self, text="Barcode:").grid(
row=0, column=0, sticky="EW", **self.options
)
self.doc_type_label = ttk.Label(self, text="Document type:").grid(
row=0, column=2, sticky=tk.W, **self.options
)
# Entries
self.barcode_entry = ttk.Entry(self)
self.barcode_entry.grid(row=0, column=1, sticky="EW", **self.options)
self.grid()
def submit(self):
self.current_dict["func"](self, self.current_frame)
class DeleteTab(ttk.Frame):
def __init__(self, container):
# Inheritance
super().__init__(container)
# self.columnconfigure(0, weight=1)
# self.columnconfigure(1, weight=1)
self.options = {"padx": 10, "pady": 10}
## Type Barcode
# Label
self.barcode_label = ttk.Label(self, text="Barcode:").grid(
row=0, column=0, sticky="EW", **self.options
)
# Entries
self.barcode_entry = ttk.Entry(self)
self.barcode_entry.grid(row=0, column=1, sticky="EW", **self.options)
# self.pack(fill="x")
self.grid(sticky="W")
class ViewTab(ttk.Frame):
def __init__(self, container):
# Inheritance
super().__init__(container)
self.container = container
self.options = {"padx": 10, "pady": 10}
# Doc types
self.doc_types = ("Incoming Bill", "Outgoing Bill", "Contract", "Other")
## Type Barcode
# Labels
# Barcode
self.barcode_label = ttk.Label(self, text="Barcode:")
self.barcode_label.grid(row=0, column=0, sticky="EW", **self.options)
# Document type
self.doc_type_label = ttk.Label(self, text="Document Type:")
self.doc_type_label.grid(row=0, column=2, sticky="EW", **self.options)
# Entries
# Barcode
self.barcode_entry = ttk.Entry(self)
self.barcode_entry.grid(row=0, column=1, sticky="EW", **self.options)
# Document type
self.doc_type_comb = ttk.Combobox(self)
self.doc_type_comb["state"] = "readonly" # Doesn't allow new entries
self.doc_type_comb["values"] = self.doc_types
self.doc_type_comb.grid(
row=0, column=3, columnspan=3, sticky=tk.EW, **self.options
)
## View Button
self.viewSubmitButton = ttk.Button(
self, text="View", command=lambda: print("View")
)
self.viewSubmitButton.grid(
column=4,
row=self.grid_size()[0],
sticky="EW",
**self.options,
)
# New LabelFrame
self.deliver_view_LabelFrame = ttk.LabelFrame(self)
self.deliver_view_LabelFrame["text"] = "View Document"
self.deliver_view_LabelFrame.grid(
row=1,
columnspan=self.grid_size()[0],
column=0,
sticky="ew",
padx=10,
pady=10,
)
# Inside LabelFrame
##TreeView
self.dataDisplay = ttk.Treeview(self.deliver_view_LabelFrame, show="")
self.dataDisplay.pack(fill="x", **self.options)
self.grid(sticky="ews")
# Create window
class MainWindow:
def __init__(self, container):
self.container = container
## Create Parent
self.tab_parent = ttk.Notebook()
self.tab_parent.enable_traversal()
# Create tab frames
self.tab_register = ttk.Frame(self.tab_parent)
self.tab_view = ttk.Frame(self.tab_parent)
self.tab_delete = ttk.Frame(self.tab_parent)
# Adding the tabs to the main object (self.tab_parent)
self.tab_parent.add(self.tab_register, text="Register Documents")
self.tab_parent.add(self.tab_delete, text="Delete Documents")
self.tab_parent.add(self.tab_view, text="View Documents")
# Create empt variables
self.current_tab = None
self.last_tab = None
# Focus on barcode
# self.register.barcode_entry.focus()
self.tab_parent.bind("<<NotebookTabChanged>>", self.on_tab_change)
# Pack notebook
# self.tab_parent.pack(expand=True, fill="x", side="left")
self.tab_parent.grid(sticky="e")
## Triggers when changing tabs
def on_tab_change(self, event):
if self.current_tab != None:
self.current_tab.destroy()
# Get the current tab name
selected = event.widget.tab("current")["text"]
# Create frame depending on Tab chosen
if selected == "Register Documents":
self.current_tab = RegisterChooseType(self.tab_register)
self.clean_tabs()
self.last_tab = self.current_tab
elif selected == "Delete Documents":
self.current_tab = DeleteTab(self.tab_delete)
self.clean_tabs()
self.last_tab = self.current_tab
elif selected == "View Documents":
self.current_tab = ViewTab(self.tab_view)
self.clean_tabs()
self.last_tab = self.current_tab
self.current_tab.barcode_entry.focus()
def clean_tabs(self):
if self.last_tab != None:
# for widget in self.last_tab.winfo_children():
# # for widget in self.last_tab.grid_slaves():
# widget.destroy()
self.last_tab.destroy()
# ## INTERESTING
# self.last_tab.blank = ttk.Label(self.last_tab, text="1")
# self.last_tab.blank.grid()
# print(self.last_tab.grid_slaves()[0].cget("text"))
# self.container.geometry("1x1")
self.tab_parent.update()
# self.container.update()
# self.container.geometry("")
# self.container.update()
# Creatig App
class App(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("")
self.title("iMPETU Document Handling v.0.1.0a")
self.resizable(0, 0)
if __name__ == "__main__":
main()
I tried deleting one by one the widgets of the current tab before changing to another one, also doing the same AFTER changing to the new tab (that's why I have 2 variables for current and last tab), I also tried leaving an empty tag (supposedly 0 pixels) but that would push all the contents down.
I tried withdraw, deiconify just out of curiosity, I also tried using self.container.update() to update the main window, also tried changing the geometry to "1x1" in the clean_tabs function and then changing again to "" at the end of the on_tab_change and see if that would maybe force it to resize but none of that worked.
You can resize all the frames (parents of instances of RegisterChooseType, DeleteTab and ViewTab) to size 1x1 at the beginning of clean_tabs():
def clean_tabs(self):
for tab in self.tab_parent.tabs():
# tab is the name of the frame
# so get the widget using .nametowidget()
# then call .config(width=1, height=1) to resize the frame to size 1x1
self.tab_parent.nametowidget(tab).config(width=1, height=1)
...

Where to initialize an instance of a class in a multiple file python code?

I working on a project that is broken into multiple files, and while I know my issue is that I'm not initializing an instance of the class I'm trying to use, I can't figure out where to do so. The two files are below - the DataLayer and the main file. The issue in question is under the first button - I'm passing data entered by the user to the DataLayer and attempting to use a method (d_read_from_pandas_all) in that class. The method's only parameter is "self." What am I missing here?
from DataLayer import *
from ViewLayer import *
class NotebookDemo(Frame):
def __init__(self, isapp=True, name='notebookdemo'):
Frame.__init__(self, name=name)
self.pack(expand=Y, fill=BOTH)
self.master.title('Inventory Data Cleaner')
self.isapp = isapp
self._create_widgets()
def _create_widgets(self):
self._create_demo_panel()
def _create_demo_panel(self):
demoPanel = Frame(self, name='demo')
demoPanel.pack(side=TOP, fill=BOTH, expand=Y)
# create the notebook
nb = Notebook(demoPanel, name='notebook')
nb.enable_traversal()
nb.pack(fill=BOTH, expand=Y, padx=2, pady=3)
self._create_descrip_tab(nb)
self._create_view_tab(nb)
self._create_text_tab(nb)
def _create_descrip_tab(self, nb):
# frame to hold contentx
frame = Frame(nb, name='descrip')
# widgets to be displayed on 'Description' tab
docName = tk.StringVar()
docLabel = tk.Label(frame, text = 'enter cruise document name')
docEntry = tk.Entry(frame, textvariable = docName)
btn = Button(frame, text='Load', underline=0,
command=lambda: [DataLayer(str(docName)), DataLayer.d_read_from_pandas_all()])
#
# position and set resize behaviour
btn.grid(row=1, column=0, pady=(2, 4))
docLabel.grid(row = 0, column = 0, pady = (2,4))
docEntry.grid(row = 0, column = 1, pady = (2,4))
frame.rowconfigure(1, weight=1)
frame.columnconfigure((0, 1), weight=1, uniform=1)
# add to notebook (underline = index for short-cut character)
nb.add(frame, text='View Data', underline=0, padding=0)
# =============================================================================
def _create_view_tab(self, nb):
# Populate the second pane. Note that the content doesn't really matter
frame = Frame(nb, name="view")
btn1 = Button(frame, text='Click here to view data', underline=0,
command=lambda:[DataLayer.d_read_from_pandas_all(self, docName), ViewLayer.create_display_table(self,frame), ViewLayer.viewTable(self)])
btn1.grid(row=1, column=0, pady=(2, 4))
nb.add(frame, text='Check Data', underline=0, padding=2)
# =============================================================================
def _create_text_tab(self, nb):
# populate the third frame with a text widget
frame = Frame(nb)
txt = Text(frame, wrap=WORD, width=40, height=10)
vscroll = Scrollbar(frame, orient=VERTICAL, command=txt.yview)
txt['yscroll'] = vscroll.set
vscroll.pack(side=RIGHT, fill=Y)
txt.pack(fill=BOTH, expand=Y)
# add to notebook (underline = index for short-cut character)
nb.add(frame, text='Text Editor', underline=0)
if __name__ == '__main__':
NotebookDemo().mainloop()
import pandas as pd
class DataLayer:
def __init__(self, filename):
self.filename = filename
def d_read_from_pandas_all(self):
global df
df = pd.read_csv(self.filename, index_col=0)
print(df.head(10))
return df
To import a Class from anothor file you need to import it like:
from file import class
this also works if you have structured your files in folders:
from folder.file import class
assuming your DataLayer class is saved in datalayer.py within the same directory you would need to import:
from datalayer import DataLayer
if you would save you seperate classes in a folder called classesthat is in the same directory like your main file, you would import:
from classes.datalayer import DataLayer

How can I return a variable from a function that is opening a file using filedialog after selecting a tkinter button?

All of the tutorials I have seen deomonstrate the tkinter filedialog.askopenfilename function by only using the information collected within the function that is linked to the tkinter button. I can pass information in the function, but I would like to pass variables (filepath, images, etc.) outside the function and have them update variables in my GUI.
I have commented out the location I would like to call the variables in main_gui_setup function below. Any help will be appreciated, as it has been very demoralizing not being able to open a file. If this problem persists, my future as a programmer may be limited to creating tic-tac-toe games or instructional videos for Youtube.
from tkinter import *
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog, messagebox
from PIL import ImageTk, Image # was import PIL.Image, PIL.ImageTk
import cv2
def main():
root = Tk()
window1 = Window(root, "X-ray Assist", "Give up")
return None
# can't pass by reference in python
class Window():
n = 0
file_path = ""
img1_info = ""
def __init__(self, root, title, message):
self.root = root
self.root.title(title)
#self.root.geometry(geometry)
self.screen_width = root.winfo_screenwidth()
self.screen_height = root.winfo_screenheight()
#self.root.attributes('-topmost', 1)
# SET APP WINDOW SIZE
scr_size_main = self.scr_size() # create instance of scr_size
self.root.geometry("%dx%d+%d+%d" % (self.root_width, self.root_height, self.root_x, self.root_y))
# CREATE MAIN WINDOW GUI
create_gui = self.main_gui_setup()
self.root.mainloop()
pass
def scr_size(self):
'''Reads monitor size and adjusts GUI frame sizes'''
self.root_width = int(self.screen_width*0.52)
self.root_height = int(self.screen_height*0.9)
self.root_x = int(self.screen_width*0.23)
self.root_y = int(self.screen_height*0.02)
self.img_ht_full = int(self.screen_height*0.82)
self.tools_nb_width = int(self.screen_width*0.22)
self.tools_nb_height = int(self.screen_height*0.48)
self.hist_nb_width = int(self.screen_width*0.22)
self.hist_nb_height = int(self.screen_height*0.23)
def open_image(self):
main_win = ttk.Frame(self.root)
main_win.grid(column=0, row=0)
self.file_path = filedialog.askopenfilename(initialdir='/', title='Open File',
filetypes=(('tif files', '*.tif'), ('all files', '*.*')))
self.file_path_label = ttk.Label(main_win, text=self.file_path)
self.file_path_label.grid(column=0, row=0, columnspan=1, sticky="nw", padx=(5,0), pady=1)
self.img1_8bit = cv2.imread(self.file_path, 0) #, cv2.IMREAD_ANYDEPTH | cv2.IMREAD_GRAYSCALE)
#self.img1_8bit_resize = cv2.resize(self.img1_8bit, (self.img_ht_full, self.img_ht_full)) #, interpolation = cv2.INTER_CUBIC)
#self.img1_height, self.img1_width = self.img1_8bit.shape # not resized for screen
#img1_info = text = f"{self.img1_height} {self.img1_8bit.dtype} {self.img1_16bit.dtype}"
#print(self.img1_width, " x ", self.img1_height, " bitdepth = ", self.img1_8bit.dtype)
#img1_info = ttk.Label
#print(f"{self.img1_height} {self.img1_width} {self.img1_8bit.dtype}")
#img1_info.grid(column=3, row=1, columnspan=1, sticky="w", padx=(5,0), pady=1)
#img = io.imread(main_win.filename) #scikit
self.img1_16bit = cv2.imread(self.file_path, cv2.IMREAD_ANYDEPTH | cv2.IMREAD_GRAYSCALE)
#self.img_canvas = tk.Canvas(self.root, width=self.img_ht_full, height=self.img_ht_full)
#self.img_canvas.grid(column=1, row=2, columnspan=10, rowspan=10, sticky="nw")
#self.img_canvas.image = ImageTk.PhotoImage(image=Image.fromarray(self.img1_8bit_resize))
#self.img_canvas.create_image(0,0, image=self.img_canvas.image, anchor="nw")
# .create_line(x1, y1, x2, y2, fill="color")
#self.img_canvas.create_line((self.img_ht_full/2), 0, (self.img_ht_full/2), (self.img_ht_full), fill="yellow")
#self.img_canvas.create_line(0, (self.img_ht_full/2), (self.img_ht_full), (self.img_ht_full/2), fill="yellow")
def main_gui_setup(self):
main_win = ttk.Frame(self.root)
main_win.grid(column=0, row=0)
image_win = ttk.Frame(main_win, borderwidth=25, relief="groove", width=self.img_ht_full, height=self.img_ht_full)
image_win.grid(column=1, row=2, columnspan=10, rowspan=10, sticky="nw")
toolbar = ttk.Frame(main_win, borderwidth=5) #, width=1100, height=15)
toolbar.grid(column=0, row=0, columnspan=10, rowspan=1, sticky="nw", padx=20)
hist_win = ttk.Frame(main_win, borderwidth=25, relief="groove", width=300, height=200)
panel_info = ttk.Label(main_win, text=f"{self.screen_width} x {self.screen_height}")
panel_info.grid(column=5, row=1, columnspan=1, sticky="e", pady=1)
# SCROLL SLIDER AT BOTTOM
slider = ttk.Scrollbar(main_win, orient="horizontal")
slider.grid(column=1, row=13, columnspan=7, padx=5, pady=5, sticky="ew")
#X-RAY AND DETECTOR SETTINGS - will input these from separate class
kv = ttk.Label(main_win, text="125kV")
kv.grid(column=0, row=2, columnspan=1, padx=15, pady=5)
file_path_label = ttk.Label(main_win, text="No image loaded")
file_path_label.grid(column=1, row=1, columnspan=1, sticky="nw", padx=(5,0), pady=1)
# CREATE BUTTONS
open = ttk.Button(toolbar, text="Open", width=10, command=self.open_image)
open.grid(column=0, row=0)
save = ttk.Button(toolbar, text="Save", width=10)
save.grid(column=1, row=0)
b1 = ttk.Button(toolbar, text="1", width=10)
b1.grid(column=2, row=0)
b2 = ttk.Button(toolbar, text="2", width=10)
b2.grid(column=3, row=0)
pass
main()
You aren't thinking of event driven programming correctly. In event driven programming you have callbacks to the functions you defined. Let's look at your code:
def get_path(self):
...
self.path_label = ...
...
def main_gui_setup(self):
main_win = ttk.Frame(self.root)
main_win.pack()
open = ttk.Button(main_win, text="Open", width=10, command=self.get_path)
open.pack()
# Problematic code:
# main_label = ttk.Label(main_win, self.path_label)
# main_label.pack()
When main_gui_setup is called it creates a frame and a button inside it. When the button is clicked it calls get_path which sets up the path_label variable. The problem that you were facing is that that as soon as you create your button (without waiting for the button to be pressed), you create the label called main_label.
For a simple fix to your problem try this:
def get_path(self):
...
self.file_path = ...
self.path_label = ...
...
def button_callback(self, main_win):
# set up
self.get_path()
# My guess is that you wanted `self.file_path` here instead of `self.path_label`
main_label = ttk.Label(main_win, self.file_path)
main_label.pack()
def main_gui_setup(self):
main_win = ttk.Frame(self.root)
main_win.pack()
# I am using a lambda and passing in `main_win` because you haven't
# assigned it to `self` using `self.main_win = ...`
open = ttk.Button(main_win, text="Open", width=10, command=lambda: self.button_callback(main_win))
open.pack()
I am still confused by what You are trying to accomplish:
from tkinter import Tk, Button, Label, filedialog
class MainWindow(Tk):
def __init__(self):
Tk.__init__(self)
self.open_file_btn = Button(self, text='Open', command=self.open_file)
self.open_file_btn.pack()
self.file_name = None
def open_file(self):
self.file_name = filedialog.askopenfilename()
Label(self, text=self.file_name).pack()
root = MainWindow()
root.mainloop()
I will explain what will happen here! (You can change the .askopenfilename() attributes obviously).
When the program opens the filedialog and You select a file, that file name will now get assigned to self.file_name and that variable can be used anywhere in the class.
also from what I have seen You should learn more about classes and PEP 8

Python tkinter - scrollbar of canvas cuts off frame content

I am currently writing a simple logbook application for my sailing holidays. Therefore I wanted to create a small gui for entering data in lines for the logbook.
I am using grid layout with frames and canvas for the main frame, to show a scroll bar, when there are to many lines.
Everything is looking fine, until the command
self.canvasCenter.configure(scrollregion=self.canvasCenter.bbox("all"))
is called. If I comment this line out, the canvas is using the full frame. If this line is active - the canvas is using only 50% of the available screen and date is cut off.
I tried to strip down the code to the necessary things (not using any additional files, configs, classes, etc.). So that it is executable stand alone:
#!/usr/bin/python
# -*- coding: iso-8859-1 -*-
import sys
if sys.version_info.major == 2:
# We are using Python 2.x
import Tkinter as tk
elif sys.version_info.major == 3:
# We are using Python 3.x
import tkinter as tk
#from lib import logBookData
#from config import *
#Titel of the application shown in title bar
appTitle = "My APP"
#Layout geometry (size of the window)
lWidth = 768
lHeight = 900
layoutGeometry = '900x768'
#Colours for foreground and background for labels and entry fields
#headerBG = "Light Blue"
headerBG = "white"
labelBG = "white"
labelFG = "black"
#Number of columns (keep in Sync with headers and width list below!)
cNumber = 14
#Column width for number of columns defined above
cWidth = [5,7,5,5,5,5,5,5,5,5,5,5,5,10]
cHeaders = ["C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10", "C11", "C12", "C13", "C14" ]
class logBookGUI(tk.Tk):
def __init__(self,parent):
tk.Tk.__init__(self,parent)
self.parent = parent
self.lineCount = 0
self.initialize()
def initialize(self):
#create a masterframe
self.masterFrame = tk.Frame(self, bg=headerBG, bd=4, relief="groove") #, width = lWidth, height = lHeight)
self.masterFrame.grid(sticky=tk.NSEW)
self.masterFrame.columnconfigure(cNumber, weight=1)
#create Headerframe for Standard Info
self.headerFrame = tk.Frame(self.masterFrame, bg=headerBG, bd=2, relief="groove")
self.headerFrame.grid(row=0, column=0, sticky=tk.NSEW)
self.headerFrame.columnconfigure(cNumber, weight=1)
#Label Bootsname
boatNameLabel = tk.Label(self.headerFrame, text="Bootsname", anchor="w", fg=labelFG,bg=labelBG)
boatNameLabel.grid(column=0, row=0, sticky=tk.NSEW)
#Field Bootsname
self.boatName = tk.StringVar()
self.boatNameEntry = tk.Entry(self.headerFrame,textvariable=self.boatName, width = cWidth[0])
self.boatNameEntry.grid(column=1,row=0, sticky=tk.NSEW)
self.boatName.set(u"Solva")
for i in xrange(cNumber-2):
button = tk.Button(self.headerFrame,text=u"T %s" %i, command=self.OnButtonClick)
button.grid(column=i,row=0, sticky=tk.NSEW)
button = tk.Button(self.headerFrame,text=u"TEST", command=self.OnButtonClick)
button.grid(column=cNumber-2,row=0, sticky=tk.NSEW)
button = tk.Button(self.headerFrame,text=u"Neue Zeile", command=self.newLineClick)
button.grid(column=cNumber-1,row=1, sticky=tk.NSEW)
#create center frame
self.centerFrame = tk.Frame(self.masterFrame, bg=headerBG, bd=2, relief="groove")
self.centerFrame.grid(row=1, column=0, sticky=tk.NSEW)
self.centerFrame.columnconfigure(cNumber, weight=1)
#create a canvas for center frame
self.canvasCenter = tk.Canvas(self.centerFrame)
self.canvasCenter.grid(row=0, column=0, sticky=tk.NSEW)
self.canvasCenter.columnconfigure(cNumber, weight=1)
# Create a vertical scrollbar linked to the canvas.
vsbar = tk.Scrollbar(self.centerFrame, orient=tk.VERTICAL, command=self.canvasCenter.yview)
vsbar.grid(row=0, column=cNumber, sticky=tk.NSEW)
self.canvasCenter.configure(yscrollcommand=vsbar.set)
# Create a frame on the canvas to contain the content.
self.contentFrame = tk.Frame(self.canvasCenter, bg="Red", bd=2, relief="groove")
self.contentFrame.grid(row=0, column=0, sticky=tk.NSEW)
self.contentFrame.columnconfigure(cNumber, weight=1)
column = 0
for header in cHeaders:
label = tk.Label(self.contentFrame, text=header, anchor="w", fg=labelFG,bg=labelBG,borderwidth=2,relief="groove", width = cWidth[column])
label.grid(column = column, row=1, sticky="EW")
column += 1
self.addLine()
# Create canvas window to hold the centerframe.
self.canvasCenter.create_window((0,0), window=self.contentFrame, anchor=tk.NW)
# Update buttons frames idle tasks to let tkinter calculate buttons sizes
self.contentFrame.update_idletasks()
self.canvasCenter.configure(scrollregion=self.canvasCenter.bbox("all"))
self.grid_columnconfigure(cNumber,weight=1)
self.resizable(True,False)
self.update()
self.geometry("%sx%s" % (lHeight, lWidth))
def newLineClick(self):
self.addLine()
def OnButtonClick(self):
pass
def OnPressEnter(self,event):
pass
def addLine(self):
tkLine = []
try:
for i in xrange(cNumber):
self.entryVar = tk.StringVar()
self.entry = tk.Entry(self.contentFrame,textvariable=self.entryVar,width=cWidth[i])
self.entry.grid(column=i, row = self.lineCount + 3, sticky='EW')
tkLine.append(self.entryVar)
self.canvasCenter.configure(scrollregion=self.canvasCenter.bbox("all"))
except IndexError:
print ("Error in config file. Number of columns and given columns do not match - check file config.py")
sys.exit()
self.update()
self.lineCount += 1
if __name__ == "__main__":
app = logBookGUI(None)
app.title(appTitle)
app.mainloop()
Could someone give me an advice how to use scrollregion, so that the whole size of the frame is used?
Many thanks,
Gernot

Pass values to python script with Tkinter

So I've written a python script that uses the PIL library to format a png passed to it. I want to make the script more user friendly and after looking around I saw the Tkinter library which seemed perfect. Basically the script has five variables that I need to pass to it for it to run. I'm currently using the raw_input() function to asking the user for the following variables:
path_to_png
title
subtitle
source
sample_size
Once received the script runs and exports the formatted png. I've used Tkinter to build those basic inputs as you can see from the picture below but I don't know how to pass the inputed text values and the file path from the choose png button to their respective variables.
from Tkinter import *
from tkFileDialog import askopenfilename
from tkMessageBox import *
app = Tk()
app.title("Picture Formatting")
app.geometry('500x350+200+200')
#
def callback():
chart_path = askopenfilename()
return
def title_data():
title_data = chart_title
return
errmsg = 'Error!'
browse_botton = Button(app, text="Choose png", width=15, command=callback)
browse_botton.pack(side='top', padx=15, pady=15)
# Get chart data
chart_title = StringVar()
title = Entry(app, textvariable = chart_title)
title.pack(padx=15, pady=15)
chart_subtitle = StringVar()
subtitle = Entry(app, textvariable = chart_subtitle)
subtitle.pack(padx=15, pady=15)
chart_source = StringVar()
source = Entry(app, textvariable = chart_source)
source.pack(padx=15, pady=15)
chart_sample_size = IntVar()
sample_size = Entry(app, textvariable = chart_sample_size)
sample_size.pack(padx=15, pady=15)
submit_button = Button(app, text="Submit", width=15)
submit_button.pack(side='bottom', padx=15, pady=15)
app.mainloop()
I have tried your code, and I added some lines:
from Tkinter import *
from tkFileDialog import askopenfilename
from tkMessageBox import *
app = Tk()
app.title("Picture Formatting")
app.geometry('500x350+200+200')
#
def callback():
global chart_path
chart_path = askopenfilename()
return
def title_data():
title_data = chart_title
return
def calculate():
chart_title = title.get()
chart_subtitle = subtitle.get()
chart_source = source.get()
chart_sample_size = sample_size.get()
print "chart_path : ", chart_path
print "chart_title : ", chart_title
print "chart_subtitle : ", chart_subtitle
print "chart_source : ", chart_source
print "chart_sample_size : ", chart_sample_size
#Call your functions here
return
errmsg = 'Error!'
# Get chart data
chart_path = ''
browse_botton = Button(app, text="Choose png", width=15, command=callback)
browse_botton.pack(side='top', padx=15, pady=15)
chart_title = StringVar()
title = Entry(app, textvariable = chart_title)
title.pack(padx=15, pady=15)
chart_subtitle = StringVar()
subtitle = Entry(app, textvariable = chart_subtitle)
subtitle.pack(padx=15, pady=15)
chart_source = StringVar()
source = Entry(app, textvariable = chart_source)
source.pack(padx=15, pady=15)
chart_sample_size = IntVar()
sample_size = Entry(app, textvariable = chart_sample_size)
sample_size.pack(padx=15, pady=15)
submit_button = Button(app, text="Submit", width=15, command=calculate)
submit_button.pack(side='bottom', padx=15, pady=15)
app.mainloop()
I think the problem you want to ask is how to get the text values in the entry widgets and get the path text from the askopenfilename() function. You can use the method Entry.get() to get the text value in certain entry widget.
And you can just use str = askopenfilename() to get the path's text value. But because this line of code is written in a function, you need to declare that it is a global variable or create a class to contain them, or the interpreter will consider that variable is an local variable and it will not be passed to the function calculate() which I added.
Since you didn't use a class to contain the variables, I also use the variables as global variables. It is not a good design. You can consider to create a class instead.
In order to receive the value from an entry widget, you would want to use the get() function. The get() function will return whatever is in the entry widget. For instance in your case: response = sample_size.get() will set the variable response to 0. See this page for more documentation on the entry widget.
Hope this helped.

Categories