Running python code through user friendly interface - python

additional question
I'm working on a project for a family member, I have fairly limited experience with Python. I have written my script on Python, and it works exactly how I need. The code takes a photo and adds a red border to either the length or the width to create a square.
My issue is my family member does not use Python, and after some light research I have come across tkinter for a GUI. I would like to have a text box where the image name can be typed, and that runs the code from the input line.
I have it where you type MyImage.PNG into the console and it saves the border version into the same file. Also, I understand how to design with tkinter--just not how to actually execute my script with the textbox/buttons. Any advice on how to achieve this?
I would like to call images from a file, without having to individually type the image name. Is that possible through an open loop possibly? Or even using a Tkinter widget to read the images file, create a list from the file, and then individually be able to select each image and hit enter.
Thank you!
from PIL import Image, ImageOps
import math
original = input("Please enter an input: ")
im = Image.open(original)
print(im.size)
print(type(im.size))
w, h = im.size
print('width: ', w)
print('height:', h)
if w>h:
x = w - h
b = math.floor(x/2)
a = 0
if w<h:
x = h - w
a = math.floor(x/2)
b = 0
def add_border(input_image, output_image, border, color=0):
img = Image.open(input_image)
if isinstance(border, int) or isinstance(border, tuple):
bimg = ImageOps.expand(img, border=border, fill=color)
else:
raise RuntimeError('Border is not an integer or tuple!')
bimg.save(output_image)
if __name__ == '__main__':
add_border(original,
output_image= 'border' + original,
border=(a,b,a,b),
color='indianred')

Tkinter has an input field function so you could do something like:
from tkinter import *
def Get_Name_Of_Picture():
original=str(PicName.get())
PicName=Entry(screen)
PicName.pack()
Button(screen, text='Enter', command=Get_Name_Of_Picture).pack(side=LEFT)
Something like that(you might have to put the rest of the code in the Get_Name_Of_Picture function) but this will allow the user to type an input into an entry field, and once they are done hit the enter button which will get the string of what was typed and so on.
If you need more advice just comment on this answer and I will try to help.

Related

Python GUI creating a list item with Text and checkboxes

I am trying to create a list like in outlook. With list items with an layout like this:
Don't get me wrong this isn't a "give me the full answer" question. I just have the problem of the right naming. I would appreciate it a lot if some could throw in the right words and I will look for them by my own.
I used tkinter at the moment but in that it seems like there isn't a solution for that.
Kind regards.
I think Tkinter can do this by using a bit of object oriented programming you could define how one list element should look like and then with static variables you could define a position of a new line relative to the position of the previous line. Something like:
from tkinter import *
class Line:
y = 0 # static variable for the y position
def __init__(self):
self.y_local = Line.y
Line.y = Line.y + 40 # increase the static variable for the next line
print(self.y_local)
def draw(self, tk, h_line="Headline", a_info="Addtional Information", date="01/01/1970"):
self.label_head = Label(text=h_line)
self.label_head.place(x=20, y=self.y)
self.label_info = Label(text=a_info)
self.label_info.place(x=20, y=self.y+20)
self.label_date = Label(text='Date')
self.label_date.place(x=200, y=self.y)
self.label_ndate = Label(text=date)
self.label_ndate.place(x=200, y=self.y+20)
self.chkbtn = Checkbutton(tk, text="Checkbox")
self.chkbtn.place(x=300, y=self.y+20)
tk = Tk()
data = [
["News", "WWIII has been avoided", "02/04/2018"],
["Python", "Python solves 42 riddles", "02/04/2018"]
]
for line in data:
temp = Line()
temp.draw(tk, line[0], line[1], line[2])
mainloop()
Hope I understood your question well. That you have a list with information and want to display that in an easy and scalable way. I have not looked to add the lines around the information as I've never done that before I know there are separators I've used vertical once before but I wouldn't be surprised if you can draw a box around each line either.

Which Tkinter functions should i use to find the coordinates of a randomly placed(in the canvas) text entry and how?

Thanks for looking into my question. I'll try to give you a big and small picture of what I'm trying to do here.
Big Picture:
So, basically I'm trying to make a simple mindmapping program where, after the first entry, every text I input unto the Entry widget is linked to the previous text via a line widget. So like this: Hello----There, and then Hello----There----Yo. Actually, I'm hoping for more modifications in the future like being able to rearrange the links via some metric I have yet explored, but this is basically it.
Small/Specific Picture:
I realize that in order to do this, I will have to find a way to acquire all the xy coordinates of every text drawn on the canvas (text I drew on the canvas by using the random function). I need the coordinates of the first text and the coordinates of the second text so I can use those to draw the line to visually link the two texts. I thought of using an array to list down all inputted text, but I realize that only stores the text and not the location of the text on the canvas. I have explored using tags, or using the coords functions or using the bbox function, but to no avail. Any clues on how to go about this? I would highly appreciate it, Thanks. :)
import Tkinter
import random
a = Tkinter.Tk()
b = Tkinter.Canvas(a, width=1000, height=500)
b.pack()
def c(event):
b.create_text(random.randint(50,940), random.randint(50,480), anchor="center", text=d.get())
f.append(d.get())
d.delete(0, 'end')
print f
#this function creates a randomly located text taken from the entry widget below and, at the same time, appends the text in the list known as f''
d = Tkinter.Entry(a)
d.pack()
d.bind("<Return>", c)
d.focus()
b.create_line(300, 250, 600, 290)
#this is my very early attempt at linking text inputted and drawn on the Canvas
f = []
a.mainloop()
Simply assign random values to variables before you use it to create text on canvas, and keep on list with object ID and text.
x = random.randint(...)
y = random.randint(...)
obj_id = b.create_text(x, y, ...)
f.append([x, y, obj_id, d.get()])
BTW: if you have obj_id then you can also do
x,y = b.coords(obj_id)

Check if Python (3.4) Tkinter Entry is Full

I am using a standard tkinter entry to input a directory path. When the user presses enter - if the physical length of the string exceeds that of the entry, I want the program to modify the displayed text to ...[end of directory]. I have the logistics of this figured out but as of yet I have no accurate way to test whether the entry box is full, or how full
Things I have tried:
Using 'PIL.ImageFont.truetype("font.otp",fontsize).size' - cannot calculate the point at which to cut the directory
Simply using the length of the string against the length of the entry- inaccurate as the font I am using (which I don't want to change if possible) varies in length with each character
Another compromise behaviour which I tried was to make the entry "look" at the start of the path. I tried inserting at, selecting at and moving the cursor to position 0 but none of these worked
You can use xview method of Entry. xview return the visible part of the text displayed in the entry. You can use it to interactively create a text that fit the entry.
Here is a quick and dirty proof of concept
from tkinter import *
root = Tk()
v = StringVar(root)
e = Entry(root,textvariable=v)
e.pack(fill=BOTH)
v.set('abcdefghijklmnopqrstuvwxyz0123456789')
new_s = None
def check_length():
global new_s
original_s = v.get()
def shorten():
global new_s
e.xview(0)
if e.xview()[1] != 1.0:
new_s = new_s[:-4] + '...'
v.set(new_s)
print("new_s: " + new_s)
e.xview(0)
e.after(0,shorten)
print(e.xview()[1])
if e.xview() != (0.0,1.0):
new_s = original_s + '...'
shorten()
b = Button(root,text="hop",command=check_length)
b.pack()
e.mainloop()

Tkinter radio button clear / reset values

I am writing a Tkinter program for the first time and have a question on radio buttons. What I am trying to do is this:
open a set of images (one at a time).
When an image is opened, annotate a value using the radio button.
Collect this value in a list
So, in this example I have 2 compounds and the list would have 2 annotations.
The problem I have is, if by mistake the user clicks radiobutton 2 instead of one, and then corrects him/herself, the list will have 4 items (3 for the first image, 1 for the second). How do I handle this, so that the list will have only 2 values?
import Tkinter as tk
from PIL import ImageTk, Image
from tkFileDialog import askopenfilename
cmp_list = ["VU435DR","VU684DR"]
li = []
li_final = []
def sel():
selection = str(var.get())
if selection == "1":
li.append("Antagonist")
elif selection == "2":
li.append("Agonist")
for i in range(len(cmp_list)):
root = tk.Tk()
var = tk.IntVar()
ig = str(cmp_list[i] + '.png')
img = ImageTk.PhotoImage(Image.open(ig))
panel = tk.Label(root,image=img)
panel.pack(side = "top",fill="none",expand="no")
#w = tk.Text(height=2,width=50)
#w.pack(side='right")
q = tk.Radiobutton(root,text="Antagonist",command=sel,value=1,variable=var)
q.pack()
r = tk.Radiobutton(root,text="Agonist",command=sel,value=2,variable=var)
r.pack()
root.mainloop()
print li
Your code is creating more than one instance of tk.Tk(). This is not how Tkinter was designed to work, and it will yield unpredictable behavior. A proper Tkinter program always has exactly one instance of tk.Tk().
If you need more than one window, for the second and subsequent windows you should create an instance of tk.Toplevel.
To answer your specific question about how to handle someone first hitting one radiobutton and then the other -- the problem is that you are unconditionally appending to your list each time they click on a radiobutton. The solution is to use some sort of flag or indicator to know whether one of the radiobuttons has been clicked, or change your code so that it doesn't matter.
Let's look at that second option - make it so it doesn't matter. When you open up a new image you can automatically append a value to your list. In this case, set it to None to say that nothing has been picked yet. Then, in sel, you would always replace the last element rather than append a new element, since you know that the last element always refers to the current compound.

The right way to find the size of text in wxPython

I have an application I am developing in wxPython. Part of the application creates a large number of TextCtrls in a grid in order to enter four-letter codes for each day of the week for an arbitrarily large list of people.
I've managed to make it work, but I'm having to do something kludgy. Specifically, I haven't found a good way to figure out how big (in pixels) a bit of text is, so I have to guess at sizing the TextCtrls in order to just fit the biggest (4 letter) code. My concern is that if the size of the text was to change, or the application was ported to something other than MSW, that my layout might break.
The solution I found was to create a StaticText object, fill it with 4 large letters, get its size, and then .Destroy() it. This works, but it really seems like a klunky solution. Does anyone have a more direct method for figuring out just how big an arbitrary text string will be in a TextCtrl? I've looked in the docs and haven't been able to find anything, and I haven't seen any examples that do precisely what I'm trying to do.
A little code sample (showing the test method) follows:
# This creates and then immediately destroys a static text control
# in order to learn how big four characters are in the current font.
testst = wx.StaticText(self, id = wx.ID_ANY, label = "XXXX")
self.fourlettertextsize = testst.GetClientSize() + (4,4)
testst.Destroy()
(The + (4,4) part is to add a little more space in the control to accommodate the cursor and a little border in the client area of the TextCtrl; this is another thing I'd rather not be doing directly.)
Create a wx.Font instance with the face, size, etc.; create a wx.DC; then call dc.GetTextExtent("a text string") on that to get the width and height needed to display that string. Set your row height and column width in the grid accordingly.
Something like:
font = wx.Font(...)
dc = wx.DC()
dc.SetFont(font)
w,h = dc.GetTextExtent("test string")
Not to take away from Paul McNett's answer, but for those not-so-familiar with wxPython here's a simple complete example:
import wx
app = wx.PySimpleApp()
font = wx.Font(pointSize = 10, family = wx.DEFAULT,
style = wx.NORMAL, weight = wx.NORMAL,
faceName = 'Consolas')
dc = wx.ScreenDC()
dc.SetFont(font)
text = 'text to test'
# (width, height) in pixels
print '%r: %s' % (text, dc.GetTextExtent(text))
Outputs: 'text to test': (84, 15)

Categories

Resources