Implementing conditionals and loops in Tkinter GUI - python-3.x

I need to add an IF statement, FOR and WHILE loop in my form, can someone add some kind of IF statment, FOR and WHILE loop in my code?
from tkinter import * # Ingress all components from Tkinter
mGui = Tk()
mGui.geometry('400x400') # The size of the form window
mGui.title('Registration Form',)
def response():
label3 = Label(text='Thank You!',fg='White', bg='Purple',font='none 16 bold').place(x=140,y=300) # The colours and font style and size used for the response
mlabel = Label(text='Registration Form',fg='White', bg='Purple',font='none 18 bold underline') # The colours and font style and size used for the form title
mlabel.pack()
mlabel2 = Label(text='Forename',fg='White', bg='Purple',font='times 14 bold').place(x=0,y=100) # The colours and font style and size used for the label
mlabel3 = Label(text='Surname',fg='White', bg='Purple',font='times 14 bold').place(x=0,y=150) # The colours and font style and size used for the label
mbutton = Button(text = 'Submit',command = response).place(x=150,y=250) # Location of the the button 'submit' using the x and y axis
mGui.configure(background='Green') # Background colour of the form
mEntry = Entry(bg='White').place(x=100,y=100)
mEntry = Entry(bg='White').place(x=100,y=150)
mGui.mainloop() # The code iterates

Tkinter programs are event-driven, like all GUIs that I know of.
The only place where you can execute your own code is in callbacks or timeouts.
If you want to check the contents of the entry windows, you could do that in at least two ways:
Write a callback function for the Submit button that retrieves the contents of the entry windows and validates them. You could e.g. use a messagebox telling the user that the entries are not valid.
You could also set up the entry windows to validate themselves when a key is pressed. I've used this to e.g. make the text or background of an entry window red if it contains invalid text. You could also put a tip in a Label below the entry windows, like "Both Forename and Surname must at least contain one character."

Related

Multiple variations of TKInter fonts

I am trying to get a better understanding of using fonts in tkinter and ttk.
My plan is to have two different styles for headings, each with their own font size. I used nametofont() to create an instance of the font and then set the size in two different styles:
labelFont = tkinter.font.nametofont('TkTextFont')
labelFont.config(weight='bold')
ttk.Style().configure("TLabel", font=labelFont, size=12)
ttk.Style().configure("heading.TLabel", font=labelFont, size=48)
then apply the styles to headings:
heading = ttk.Label(root, text="Heading", style="heading.TLabel")
label = ttk.Label(root, text="Label", style="TLabel") # is style redundant?
Unfortunately, I don’t get two different sizes, so this is obviously the wrong approach.
I also tried something like this:
labelFont = tkinter.font.nametofont('TkTextFont')
headingFont = tkinter.font.nametofont('TkTextFont')
# etc
thinking that I would get two independent instance of the font, but they appear to be the same instance. If they were independent, I could have used configure() to give each of them their own font size.
I took this approach because I wanted to use the built-in named font, and use variables to maintain consistency. What is the correct approach to this?
You need to use .config(size=...) on the different instances of Font:
labelFont = tkinter.font.nametofont('TkTextFont')
labelFont.config(weight='bold', size=12)
# create a clone of labelFont using Font.copy()
headingFont = labelFont.copy()
headingFont.config(size=48)
s = ttk.Style()
s.configure('TLabel', font=labelFont) # apply to all instance of ttk.Label
s.configure('heading.TLabel', font=headingFont, size=96)
heading = ttk.Label(root, text='Heading', style='heading.TLabel')
label = ttk.Label(root, text='Label') # style='TLabel' is not necessary

Had an issue with tkinter displaying my label on a top level window. A syntax error fixed it?

I am working on a color matching learning game. When you click the colored square, a pop up window will show you the name of the color, typed in the corresponding color, as well as pronounce the color.
During development, I ran into an issue where tkinter would not display my .gif image (which is the typed color) on the top level window, which opens after a color is selected.
I started adding additional widgets to the top level window to attempt to figure out the issue.
However, I accidentally left of the parentheses at the end of pack on the second label I added.
Sure enough, my image showed.
When I went back to add the missing parentheses, my image stopped showing??
def callback(event):
print ("clicked at", event.x, event.y)
if 30 < event.x < 120:
print("Red")
top = tk.Toplevel()
diagrams = tk.PhotoImage(file='Red.gif')
logolbl= tk.Label(top, text = "Red", image = diagrams).pack()
btn = tk.Button(top, text="Back").pack()
##### this is the label I am referring to #####
tk.Label(top, text = "why does this fix it?").pack
if 150 < event.x <240 :
print("Green")
if 270 < event.x < 370:
print("Blue")
This is a screen shot of it displaying the image, with the missing parentheses
This is a screen shot of the second label commented out, and my image not showing
Has anyone experienced this?
I mean technically I can work with this, however, I understand it is incorrect.
I'd like to have an understanding of what I am doing wrong, and what is happening "behind the scenes"

Tkinter both 'sticky' and 'rowconfigure' did not fill the empty space

I have 2 frames inside root which called "header_frame" and "activity_frame" both are in the same column which is "column=0". I want both frames to be resizeable matching its root parent filling all empty space like this :
I have tried all grid config possibilities including setting 'rowconfigure' on 'root', set activity_frame to stick on "North" which is header_frame, last but not least I've also tried to stick header_frame to south which is the result I do not want because those frame share same size (I hope header_frame has 'maxsize' attribute but sadly it didn't have). so this is the code that I've tried :
self.root = root
self.column = ""
self.search = ""
self.root.minsize(500,480)
self.comboboxValue = None
self.root.title("CUCI (CSV Unique Column Identifier)")
self.root.configure(background="powder blue")
self.root.grid_columnconfigure(0,weight=1)
self.root.grid_rowconfigure(0,weight=1)
self.root.grid_rowconfigure(1,weight=1)
#header frame
self.header_frame = tk.Frame(self.root)
self.header_frame.grid(row=0, column=0,sticky="NEW")
self.header_frame.grid_columnconfigure(0,weight=1)
self.header_frame.grid_rowconfigure(0,weight=1)
self.header_frame.configure(background="grey")
#activity Frame
self.activity_frame = tk.Frame(self.root)
self.activity_frame.grid(row=1, column=0,sticky="NEWS")
self.activity_frame.grid_columnconfigure(0,weight=1)
self.activity_frame.grid_rowconfigure(0,weight=1)
self.activity_frame.configure(background="grey",pady=1)
Here's the layout result from my code which I do not expect:
The point is that I want to fill those empty spaces with activity_frame to be stick-ed on 'header_frame'.Please I do not wish to use pack(self.activity_frame.pack(fill=tk.X)). I just want to use grid because it's easy to use
The reason for the gap is because you don't have the header frame stick to the bottom of the space it was given. If you change the sticky attribute for the header to be "nsew" you'll see that the header fills the extra space.
self.header_frame.grid(row=0, column=0,sticky="nesw")
I'm guessing you don't want the header frame to be so tall. If that is the case, give row 0 a weight of 0 instead of 1. That way all extra unallocated space will be given to row 1.
self.root.grid_rowconfigure(0,weight=0)
After doing so, and after adding a couple of other widgets to simulate your screen, this is what it looks like:

Interactive labeling of images in jupyter notebook

I have a list of pictures:
pictures = {im1,im2,im3,im4,im5,im6}
Where
im1:
im2:
im3:
im4:
im5:
im6:
I want to assign the pictures to labels (1,2,3,4 etc.)
For instance, here pictures 1 to 3 belong to label 1, picture 4 belongs to label 2, picture 5 to label 3, and picture 6 to label 4.
-> label = {1,1,1,2,3,4}
Since I need to see the images when I label them, I need a method to do that while labeling them. I was thinking of creating an array of images:
And then I define the ranges by clicking on the first and last picture belonging to the same labels, so for example:
What do you think ? Is this somehow possible ?
I would like to assign different labels to different ranges of pictures.
For instance: When one has finished selecting the first label one could indicate it by a Double-click and then do the selection of the second label range, then Double-click, then do the selection of the third label range, then Double-click, then do the selection of the fourth label range, etc.
It does not have to be Double-clicking to change the selection of the labels, it could also just be a buttom or any other idea that you might have.
In the end one should have the list of labels.
Essentially, most of the interaction you are looking for boils down to being able to display images, and detect clicks on them in real time. As that is the case, you can use the jupyter widgets (aka ipywidgets) module to achieve most (if not all) of what you are looking for.
Take a look at the button widget which is described here with explanation on how to register to its click event. The problem - we can't display an image on a button, and I didn't find any way to do this within the ipywidgets documentation. There is an image widget, but it does not provide an on_click event. So construct a custom layout, with a button underneath each image:
COLS = 4
ROWS = 2
IMAGES = ...
IMG_WIDTH = 200
IMG_HEIGHT = 200
def on_click(index):
print('Image %d clicked' % index)
import ipywidgets as widgets
import functools
rows = []
for row in range(ROWS):
cols = []
for col in range(COLS):
index = row * COLS + col
image = widgets.Image(
value=IMAGES[index], width=IMG_WIDTH, height=IMG_HEIGHT
)
button = widgets.Button(description='Image %d' % index)
# Bind the click event to the on_click function, with our index as argument
button.on_click(functools.partial(on_click, index))
# Create a vertical layout box, image above the button
box = widgets.VBox([image, button])
cols.append(box)
# Create a horizontal layout box, grouping all the columns together
rows.append(widgets.HBox(cols))
# Create a vertical layout box, grouping all the rows together
result = widgets.VBox(rows)
You can technically also write a custom widget to display an image and listen for a click, but I simply don't believe it's worth your time and effort.
Good luck!
The qsl package provides widgets that do this. For your case, the following code would allow you to label images in batches. Full disclosure, qsl is a project I started because I, like you, wanted to label images from inside Jupyter notebooks.
import qsl
from IPython.display import display
labeler = qsl.MediaLabeler(
items=[
{"target": "https://i.stack.imgur.com/cML6z.jpg"},
{"target": "https://i.stack.imgur.com/6EVAP.jpg"},
{"target": "https://i.stack.imgur.com/CAxUw.jpg"},
{"target": "https://i.stack.imgur.com/8fhan.jpg"},
{"target": "https://i.stack.imgur.com/eMXn5.jpg"},
{"target": "https://i.stack.imgur.com/YFBfM.jpg"}
],
# Optional, you can also configure the labeler from
# the UI.
config={
"image": [
{
"name": "Type",
"options": [
{"name": "Foo"},
{"name": "Bar"}
]
}
]
},
# Optional, set to 1 if you want to label
# one image at a time.
batch_size=4,
# Optionally, save labels to JSON. You
# can also get the labels using `labeler.items`.
jsonpath="labels.json"
)
display(labeler)
This generates a UI that looks like this.
Here is a Google Colab notebook that shows how to do this in Google Colab.

Display number in rectangle

Note that I am using Python3 and Phoenix.
I would like to display a number (double, but that does not matter now) formatted in some way (again, no matter what that way is) within a rectangle: almost a wx.StaticText but not editable by the user. This is to display some data coming from some hardware, such as a temperature.
Is there such a widget?
I tried with using the default wx.StaticText with a style but I must have done something wrong:
hbox = wx.BoxSizer(wx.HORIZONTAL)
title = wx.StaticText(parent, label=label)
title.SetLabelMarkup("<b>{}</b>".format(label))
hbox.Add(title, border=5)
value = wx.StaticText(parent, label="3.141592", style=wx.BORDER_RAISED)
value.SetWindowStyle(wx.BORDER_SIMPLE)
hbox.Add(value, border=5)
title = wx.StaticText(parent, label="\u2103")
hbox.Add(title, border=5)
Shows this on Linux (Fedora 24, GTK):
Wouldn't using a wx.TextCtrl set to read only do the job?
Temp = wx.TextCtrl(panel1, value="3.141592", style=wx.TE_READONLY)
Temp.SetBackgroundColour('green')
The simplest solution is to just use wxStaticText with a border style (e.g. wxBORDER_SIMPLE, ...). If you don't like the appearance this results in, it's pretty simple to make your own widget drawing whatever border you desire: just create a window, define its wxEVT_PAINT handler and draw the (presumably centered) text in it and a border outside of it.

Resources