Python menu from list in Tkinter - mysql

Here I have a list which I am getting from the cursor of MYSQL connector. I have no problem in fetching the data. But I am interested in making a menu using the OptionMenu feature of tkinter with the data I received. Here, is what I am trying
from tkinter import *
r = Tk()
name_ = StringVar()
name = [('John',),('Jimmy',),('Smith',),('Rosel',)]
#Here I am directly giving the list, but this the output which I get from cursor of MYSQL
l=[]
for i in range(len(name)-1):
l.append(name[i][0])
print(l)
t = tuple(l)
name_.set('Select Student')
option = OptionMenu(r,name_,l)
option.pack()
But I don't think this helps as what I get is the whole list printed as a single option.
Please help me.....

You need to use python's * operator to expand the list when calling OptionMenu (eg: *l):
option = OptionMenu(r, name_, *l)

I hope it helps:
from tkinter import *
from tkinter import ttk
root = Tk()
root.title("mysql")
root.geometry(("400x400"))
class Mysql:
def __init__(self,parent):
self.parent = parent
self.options = [('John',),('Jimmy',),('Smith',),('Dave',)]
self.var = StringVar(self.parent)
self.var.set("Select Student")
self.option = ttk.OptionMenu(self.parent,self.var,*self.options)
self.option.pack()
Mysql(root)
root.mainloop()

Please let me know if this is not what you want:
python 3
from tkinter import *
from tkinter import ttk
root = Tk()
root.title("mysql")
root.geometry("400x400")
var = StringVar(root)
var.set("select student")
option = ttk.OptionMenu(root,var,'','John','Jimmy','Smith','Rosel')
option["width"] = 20
option.pack()
root.mainloop()

Related

How do I store a contentfile into ImageField in Django

I am trying to convert an image uploaded by user into a PDF , and then store it into an ImageField in a mysql database ,using a form, but am facing an error when trying to store the PDF into the database
My views.py is:
from django.core.files.storage import FileSystemStorage
from PIL import Image
import io
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.core.files.base import ContentFile
def formsubmit(request): #submits the form
docs = request.FILES.getlist('photos')
print(docs)
section = request.POST['section']
for x in docs:
fs = FileSystemStorage()
print(type(x.size))
img = Image.open(io.BytesIO(x.read()))
imgc = img.convert('RGB')
pdfdata = io.BytesIO()
imgc.save(pdfdata,format='PDF')
thumb_file = ContentFile(pdfdata.getvalue())
filename = fs.save('photo.pdf', thumb_file)
linkobj = Link(link = filename.file, person = Section.objects.get(section_name = section), date = str(datetime.date.today()), time = datetime.datetime.now().strftime('%H:%M:%S'))
linkobj.save()
count += 1
size += x.size
return redirect('index')
My models.py:
class Link(models.Model):
id = models.BigAutoField(primary_key=True)
person = models.ForeignKey(Section, on_delete=models.CASCADE)
link = models.ImageField(upload_to= 'images', default = None)
date = models.CharField(max_length=80, default = None)
time = models.CharField(max_length=80,default = None)
Error I am getting is:
AttributeError: 'str' object has no attribute 'file'
Other methods I have tried:
1) linkobj = Link(link = thumb_file, person = Section.objects.get(section_name = section), date = str(datetime.date.today()), time = datetime.datetime.now().strftime('%H:%M:%S'))
RESULT OF ABOVE METHOD:
1)The thumb_file doesnt throw an error, rather it stores nothing in the database
Points I have noticed:
1)The file is being stored properly into the media folder, ie: I can see the pdf getting stored in the media folder
How do I solve this? Thank you
You don't (basically ever) need to initialize a Storage by yourself. This holds especially true since the storage for the field might not be a FileSystemStorage at all, but could e.g. be backed by S3.
Something like
import datetime
import io
from PIL import Image
from django.core.files.base import ContentFile
def convert_image_to_pdf_data(image):
img = Image.open(io.BytesIO(image.read()))
imgc = img.convert("RGB")
pdfdata = io.BytesIO()
imgc.save(pdfdata, format="PDF")
return pdfdata.getvalue()
def formsubmit(request): # submits the form
photos = request.FILES.getlist("photos") # list of UploadedFiles
section = request.POST["section"]
person = Section.objects.get(section_name=section)
date = str(datetime.date.today())
time = datetime.datetime.now().time("%H:%M:%S")
count = 0
size = 0
for image in photos:
pdfdata = convert_image_to_pdf_data(image)
thumb_file = ContentFile(pdfdata, name="photo.pdf")
Link.objects.create(
link=thumb_file,
person=person,
date=date,
time=time,
)
count += 1
size += image.size
return redirect("index")
should be enough here, i.e. using a ContentFile for the converted PDF content; the field should deal with saving it into the storage.
(As an aside, why are date and time stored separately as strings? Your database surely has a datetime type...)
Ok so I found an answer, to be fair I wont accept my own answer as it doesn't provide an exact answer to the question I asked, rather its a different method, so if anyone does know , please do share so that the community can benefit:
My Solution:
Instead of using ContentFile, I used InMemoryUploadedFile, to store the converted pdf and then moved it into the database( in an ImageField)
I am going to be honest, I am not completely sure about why ContentFile was not working, but when going through the documentation I found out that :
The ContentFile class inherits from File, but unlike File it operates on string content (bytes also supported), rather than an actual file.
Any detailed explanation is welcome
My new views.py
from django.core.files.storage import FileSystemStorage
from PIL import Image
import io
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.core.files.base import ContentFile
import sys
def formsubmit(request): #submits the form
docs = request.FILES.getlist('photos')
print(docs)
section = request.POST['section']
for x in docs:
fs = FileSystemStorage()
print(type(x.size))
img = Image.open(io.BytesIO(x.read()))
imgc = img.convert('RGB')
pdfdata = io.BytesIO()
imgc.save(pdfdata,format='PDF')
thumb_file = InMemoryUploadedFile(pdfdata, None, 'photo.pdf', 'pdf',sys.getsizeof(pdfdata), None)
linkobj = Link(link = thumb_file, person = Section.objects.get(section_name = section), date = str(datetime.date.today()), time = datetime.datetime.now().strftime('%H:%M:%S'))
linkobj.save()
count += 1
size += x.size
return redirect('index')
If you have a question, you can leave it in the comments and ill try to answer it, Good luck!!!

How to let options of panel.widgets.MultiSelect depend on other widget inputs

I have a multiple select widget, pn.widgets.MultiSelect, and I am trying to get that when its value is selected it updates the options of another pn.widgets.MultiSelect. This is very similar to How do i automatically update a dropdown selection widget when another selection widget is changed? (Python panel pyviz), however, I have not had much success implementing it.
Using the data from the above example, but as a pandas dataframe, when I try to update the options nothing happens:
outerType = pn.widgets.MultiSelect(name="Outer",
value=["Africa"],
options=np.unique(df.cont).tolist())
innerType = pn.widgets.MultiSelect(name="Inner",
options=np.unique(df.loc[df['cont'].isin(outerType.value)].country).tolist())
#pn.depends(outerType, watch=True)
def _update(outerType):
_values = np.unique(df.loc[df['cont'].isin(outerType.value)].country).tolist()
innerType.options = _values
innerType.value = _values
pn.Row(outerType, innerType)
The same happens if I use
#pn.depends(outerType.param.value, watch=True)
def _update(outerType):
However, it does execute as I expect if I use the code below; this uses AnyOldDummy as the argument in def. How can I do this properly please?
outerType = pn.widgets.MultiSelect(name="Outer",
value=["Africa"],
options=np.unique(df.cont).tolist())
innerType = pn.widgets.MultiSelect(name="Inner",
options=np.unique(df.loc[df['cont'].isin(outerType.value)].country).tolist() )
#pn.depends(outerType, watch=True)
def _update(AnyOldDummy):
_values = np.unique(df.loc[df['cont'].isin(outerType.value)].country).tolist()
innerType.options = _values
innerType.value = _values
pn.Row(outerType, innerType)
Data:
import numpy as np
import pandas as pd
import panel as pn
pn.extension()
_countries = {
'Africa': ['Ghana', 'Togo', 'South Africa'],
'Asia' : ['China', 'Thailand', 'Japan'],
'Europe': ['Austria', 'Bulgaria', 'Greece']
}
df = []
for cont in _countries.keys():
co = _countries.get(cont)
df.append(np.c_[np.repeat(cont, len(co)), co])
df = pd.DataFrame(np.vstack(df), columns=['cont', 'country'])

Iterating through multiline input, and match to database items

I need help iterating through input to a webapp I'm writing, which looks like:
The users will be inputting several hundred (or thousands) of urls pasted from excel documents, each on a new line like this. Thus far, as you can see, I've created the input page, an output page, and written the code to query the database.
from flask import Flask,render_template, request
from flask_sqlalchemy import SQLAlchemy
from urllib.parse import urlparse
from sqlalchemy.ext.declarative import declarative_base
app = Flask(__name__)
app.config["DEBUG"] = True
app.config["SECRET_KEY"] = "secret_key_here"
db = SQLAlchemy(app)
SQLALCHEMY_DATABASE_URI = db.create_engine(connector_string_here))
app.config[SQLALCHEMY_DATABASE_URI] = SQLALCHEMY_DATABASE_URI
app.config["SQLALCHEMY_POOL_RECYCLE"] = 299
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db.Model = declarative_base()
class Scrapers(db.Model):
__tablename__ = "Scrapers"
id = db.Column(db.Integer, primary_key = True)
scraper_dom = db.Column(db.String(255))
scraper_id = db.Column(db.String(128))
db.Model.metadata.create_all(SQLALCHEMY_DATABASE_URI)
Session = db.sessionmaker()
Session.configure(bind=SQLALCHEMY_DATABASE_URI)
session = Session()
scrapers = session.query(Scrapers.scraper_dom, Scrapers.scraper_id).all()
#app.route("/", methods=["GET","POST"])
def index():
if request.method == "Get":
return render_template("url_page.html")
else:
return render_template("url_page.html")
#app.route("/submit", methods=["GET","POST"])
def submit():
sites = [request.form["urls"]]
for site in sites:
que = urlparse(site).netloc
return render_template("submit.html", que=que)
#scrapers.filter(Scrapers.scraper_dom.in_(
#next(x.scraper_id for x in scrapers if x.matches(self.fnetloc))
As is apparent, this is incomplete. I've omitted previous attempts at matching the input, as I realized I had issues iterating through the input. At first, I could only get it to print all of the input instead of iterating over it. And now, it prints like this:
Which is just repeating the urlparse(site).netloc for the first line of input, some random number of times. It is parsing correctly and returning the actual value I will need to use later (for each urlparse(site).netloc match scraper_dom and return associated scraper_id). Now, though, I've tried using input() but kept getting errors with [request.form["urls"]] not being an iterable.
Please help, it'd be much appreciated.
Output of sites:
New output with:
que = [urlparse(site).netloc for site in request.form["urls"].split('\n')]

Python3 tkinter label value

I need some help, I trying to update the etcprice label value after I push the button and after every 5 seconds, in terminal works, but in tk window not. I stucked here :( please, help me.
I tried to setup the "price" to "StringVar()" but in that case I got a lot of errors.
Many thanks
import urllib.request
from urllib.request import *
import json
import six
from tkinter import *
import tkinter as tk
import threading
price = '0'
def timer():
threading.Timer(5.0, timer).start()
currentPrice()
def currentPrice():
url = 'https://api.cryptowat.ch/markets/bitfinex/ethusd/price'
json_obj = urllib.request.urlopen(url)
data = json.load(json_obj)
for item, v in six.iteritems(data['result']):
# print("ETC: $", v)
price = str(v)
# print(type(etcar))
print(price)
return price
def windows():
root = Tk()
root.geometry("500x200")
kryptoname = Label(root, text="ETC price: ")
kryptoname.grid(column=0, row=0)
etcprice = Label(root, textvariable=price)
etcprice.grid(column=1, row=0)
updatebtn = Button(root, text="update", command=timer)
updatebtn.grid(column=0, row=1)
root.mainloop()
windows()
The solution was: I created a new String variable called “b” and I changed the etcprice Label variable to this.
After I added this b.set(price) in currentPrice() def: and is working.
The price variable is a global - if you're trying to change it, you need to do so explicitly:
def currentPrice():
global price
url = 'https://api.cryptowat.ch/markets/bitfinex/ethusd/price'
json_obj = urllib.request.urlopen(url)
data = json.load(json_obj)
for item, v in six.iteritems(data['result']):
# print("ETC: $", v)
price = str(v)
# print(type(etcar))
print(price)
return price
otherwise, Python will 'mirror' it as a local variable inside the function, and not modify the global.
It's not a good idea to keep on launching more and more threads each time you click the button - so:
updatebtn = Button(root, text="update", command=currentPrice)
probably makes more sense.
You don't need to use threads here, just to call functions 'in the background'. You can use tkinter's own .after function instead to delay caling functions. (it uses milliseconds, not float second values, btw)
def timer(delay_ms, root, func):
func()
root.after(delay_ms, timer, root, func)
might be a helpful kind of function.
Then before you launch your mainloop, or whenever you want the getting to start, call it once:
timer(5000, root, currentPrice)
If you want the currentPrice function to run in a separate thread, and so not block your main GUI thread if there is network lag, for instance, then you can use threads more like this:
Thread(target=currentPrice, daemon=True).start()
which will run it in a daemon-thread - which will automatically get killed if you close the program, or ctrl-c it, or whatever. So you could put that line in a getCurrentPriceBG or similar function.

save list to CSV - python

I try to save my output as csv using the "import csv" and only get errors. Any reason why?
Since I can not make it run will it also notify if the file already exists?
Thanks a lot
from tkinter import *
from tkinter.filedialog import asksaveasfilename
from tkinter import ttk
import csv
def data():
...
output= <class 'list'> #just an example
...
def savefile():
name= asksaveasfilename()
create = csv.writer(open(name, "wb"))
create.writerow(output)
for x in output:
create.writerow(x)
root = Tk()
Mframe = ttk.Frame(root)
Mframe.grid(column=0, row=0, sticky=(N, W, E, S))
bSave=ttk.Button(Mframe, text='Save File', command=savefile)
bSave.grid(column=1, row=0)
root.mainloop()
You are opening the file, but not closing it. A good practise is to use a with statement make sure you close it. By the way, I don't know how is the output list, but if it isn't a list of lists, it makes more sense to me to call writerow once.
Besides, make sure this list is also a global variable, otherwise it won't be available within the scope of savefile. However, global variables are not a very good solution, so consider to pass it as an argument to savefile or use a class to hold all this data:
def savefile():
name = asksaveasfilename()
with open(name, 'w', newline='') as csvfile:
create = csv.writer(csvfile)
create.writerow(output)