Tkinter dropdown menu from SQL table - mysql

Instead of having a user input "Job Name", I want it to be a Tkinter drop down menu.
Im guessing the idea will be to append Jobname with the list of names from Table. How will it have it show in the tkinter dropdown?
Jobname = []
...
for row in rows:
data = "%s %s %s %s %s" % (row["Id"], row["Title"], row["Date"], row["Time"], row["Duration"])
movieList.append(data)
print data
My Code
def reportdata():
clear()
w11 = Label(fr, text="Job Name : ")
w11.grid(row=1, column=0)
listlab.append(w11)
w22 = Label(fr, text="Job Start Date: ")
w22.grid(row=2, column=0)
listlab.append(w22)
w33 = Label(fr, text="Job End Date: ")
w33.grid(row=3, column=0)
listlab.append(w33)
e11 = Entry(fr, textvariable=jjname)
e11.grid(row=1, column=1)
listlab.append(e11)
e22 = Entry(fr, textvariable=jjdates)
e22.grid(row=2, column=1)
listlab.append(e22)
e33 = Entry(fr, textvariable=jjdatee)
e33.grid(row=3, column=1)
listlab.append(e33)
mbuttonn = Button(fr, text="Generate", command=savexl)
mbuttonn.grid(row=4, column=3)
listlab.append(mbuttonn)

Please see below for an example of how you can use a list to populate a tkinter OptionMenu:
from tkinter import *
root = Tk()
a = [] #creates a list to store the job names in
var = StringVar() #creates a stringvar to store the value of options
for i in range(20): #fills list with nonsense jobs for troubleshooting
a.append("Job Name "+str(i))
var.set(a[0]) #sets the default option of options
options = OptionMenu(root, var, *a) #createa an optionmenu populated with every element of the list
button = Button(root, text="Ok", command=lambda:print(var.get())) #prints the current value of options
options.pack()
button.pack()
This uses a list to fill out an OptionMenu with all of it's elements, then we create a button which prints the current value of the OptionMenu.
If you can get your SQL imported data into a list you can use the same logic as above.

Related

Plotly Express: Prevent bars from stacking when Y-axis catgories have the same name

I'm new to plotly.
Working with:
Ubuntu 20.04
Python 3.8.10
plotly==5.10.0
I'm doing a comparative graph using a horizontal bar chart. Different instruments measuring the same chemical compounds. I want to be able to do an at-a-glance, head-to-head comparison if the measured value amongst all machines.
The problem is; if the compound has the same name amongst the different instruments - Plotly stacks the data bars into a single bar with segment markers. I very much want each bar to appear individually. Is there a way to prevent Plotly Express from automatically stacking the common bars??
Examples:
CODE
gobardata = []
for blended_name in _df[:20].blended_name: # should always be unique
##################################
# Unaltered compound names
compound_names = [str(c) for c in _df[_df.blended_name == blended_name]["injcompound_name"].tolist()]
# Random number added to end of compound_names to make every string unique
# compound_names = ["{} ({})".format(str(c),random.randint(0, 1000)) for c in _df[_df.blended_name == blended_name]["injcompound_name"].tolist()]
##################################
deltas = _df[_df.blended_name == blended_name]["delta_rettime"].to_list()
gobardata.append(
go.Bar(
name = blended_name,
x = deltas,
y = compound_names,
orientation='h',
))
fig = go.Figure(data = gobardata)
fig.update_traces(width=1)
fig.update_layout(
bargap=1,
bargroupgap=.1,
xaxis_title="Delta Retention Time (Expected - actual)",
yaxis_title="Instrument name(Injection ID)"
)
fig.show()
What I'm getting (Using actual, but repeated, compound names)
What I want (Adding random text to each compound name to make it unique)
OK. Figured it out. This is probably pretty klugy, but it consistently works.
Basically...
Use go.FigureWidget...
...with make_subplots having a common x-axis...
...controlling the height of each subplot based on number of bars.
Every bar in each subplot is added as an individual trace...
...using a dictionary matching bar name to a common color.
The y-axis labels for each subplot is a list containing the machine name as [0], and then blank placeholders ('') so the length of the y-axis list matches the number of bars.
And manually manipulating the legend so each bar name appears only once.
# Get lists of total data
all_compounds = list(_df.injcompound_name.unique())
blended_names = list(_df.blended_name.unique())
#################################################################
# The heights of each subplot have to be set when fig is created.
# fig has to be created before adding traces.
# So, create a list of dfs, and use these to calculate the subplot heights
dfs = []
subplot_height_multiplier = 20
subplot_heights = []
for blended_name in blended_names:
df = _df[(_df.blended_name == blended_name)]#[["delta_rettime", "injcompound_name"]]
dfs.append(df)
subplot_heights.append(df.shape[0] * subplot_height_multiplier)
chart_height = sum(subplot_heights) # Prep for the height of the overall chart.
chart_width = 1000
# Make the figure
fig = make_subplots(
rows=len(blended_names),
cols=1,
row_heights = subplot_heights,
shared_xaxes=True,
)
# Create the color dictionary to match a color to each compound
_CSS_color = CSS_chart_color_list()
colors = {}
for compound in all_compounds:
try: colors[compound] = _CSS_color.pop()
except IndexError:
# Probably ran out of colors, so just reuse
_CSS_color = CSS_color.copy()
colors[compound] = _CSS_color.pop()
rowcount = 1
for df in dfs:
# Add bars individually to each subplot
bars = []
for label, labeldf in df.groupby('injcompound_name'):
fig.add_trace(
go.Bar(x = labeldf.delta_rettime,
y = [labeldf.blended_name.iloc[0]]+[""]*(len(labeldf.delta_rettime)-1),
name = label,
marker = {'color': colors[label]},
orientation = 'h',
),
row=rowcount,
col=1,
)
rowcount += 1
# Set figure to FigureWidget
fig = go.FigureWidget(fig)
# Adding individual traces creates redundancies in the legend.
# This removes redundancies from the legend
names = set()
fig.for_each_trace(
lambda trace:
trace.update(showlegend=False)
if (trace.name in names) else names.add(trace.name))
fig.update_layout(
height=chart_height,
width=chart_width,
title_text="∆ of observed RT to expected RT",
showlegend = True,
)
fig.show()

Is there a way to take a list of strings and create a JSON file, where both the key and value are list items?

I am creating a python script that can read scanned, and tabular .pdfs and extract some important data and insert it into a JSON to later be implemented into a SQL database (I will also be developing the DB as a project for learning MongoDB).
Basically, my issue is I have never worked with any JSON files before but that was the format I was recommended to output to. The scraping script works, the pre-processing could be a lot cleaner, but for now it works. The issue I run into is the keys, and values are in the same list, and some of the values because they had a decimal point are two different list items. Not really sure where to even start.
I don't really know where to start, I suppose since I know what the indexes of the list are I can easily assign keys and values, but then it may not be applicable to any .pdf, that is the script cannot be coded explicitly.
import PyPDF2 as pdf2
import textract
with "TestSpec.pdf" as filename:
pdfFileObj = open(filename, 'rb')
pdfReader = pdf2.pdfFileReader(pdfFileObj)
num_pages = pdfReader.numpages
count = 0
text = ""
while count < num_pages:
pageObj = pdfReader.getPage(0)
count += 1
text += pageObj.extractText()
if text != "":
text = text
else:
text = textract.process(filename, method="tesseract", language="eng")
def cleanText(x):
'''
This function takes the byte data extracted from scanned PDFs, and cleans it of all
unnessary data.
Requires re
'''
stringedText = str(x)
cleanText = stringedText.replace('\n','')
splitText = re.split(r'\W+', cleanText)
caseingText = [word.lower() for word in splitText]
cleanOne = [word for word in caseingText if word != 'n']
dexStop = cleanOne.index("od260")
dexStart = cleanOne.index("sheet")
clean = cleanOne[dexStart + 1:dexStop]
return clean
cleanText = cleanText(text)
This is the current output
['n21', 'feb', '2019', 'nsequence', 'lacz', 'rp', 'n5', 'gat', 'ctc', 'tac', 'cat', 'ggc', 'gca', 'cat', 'ttc', 'ccc', 'gaa', 'aag', 'tgc', '3', 'norder', 'no', '15775199', 'nref', 'no', '207335463', 'n25', 'nmole', 'dna', 'oligo', '36', 'bases', 'nproperties', 'amount', 'of', 'oligo', 'shipped', 'to', 'ntm', '50mm', 'nacl', '66', '8', 'xc2', 'xb0c', '11', '0', '32', '6', 'david', 'cook', 'ngc', 'content', '52', '8', 'd260', 'mmoles', 'kansas', 'state', 'university', 'biotechno', 'nmolecular', 'weight', '10', '965', '1', 'nnmoles']
and we want the output as a JSON setup like
{"Date | 21feb2019", "Sequence ID: | lacz-rp", "Sequence 5'-3' | gat..."}
and so on. Just not sure how to do that.
here is a screenshot of the data from my sample pdf
So, i have figured out some of this. I am still having issues with grabbing the last 3rd of the data i need without explicitly programming it in. but here is what i have so far. Once i have everything working then i will worry about optimizing it and condensing.
# for PDF reading
import PyPDF2 as pdf2
import textract
# for data preprocessing
import re
from dateutil.parser import parse
# For generating the JSON file array
import json
# This finds and opens the pdf file, reads the data, and extracts the data.
filename = "*.pdf"
pdfFileObj = open(filename, 'rb')
pdfReader = pdf2.PdfFileReader(pdfFileObj)
text = ""
pageObj = pdfReader.getPage(0)
text += pageObj.extractText()
# checks if extracted data is in string form or picture, if picture textract reads data.
# it then closes the pdf file
if text != "":
text = text
else:
text = textract.process(filename, method="tesseract", language="eng")
pdfFileObj.close()
# Converts text to string from byte data for preprocessing
stringedText = str(text)
# Removed escaped lines and replaced them with actual new lines.
formattedText = stringedText.replace('\\n', '\n').lower()
# Slices the long string into a workable piece (only contains useful data)
slice1 = formattedText[(formattedText.index("sheet") + 10): (formattedText.index("secondary") - 2)]
clean = re.sub('\n', " ", slice1)
clean2 = re.sub(' +', ' ', clean)
# Creating the PrimerData dictionary
with open("PrimerData.json",'w') as file:
primerDataSlice = clean[clean.index("molecular"): -1]
primerData = re.split(": |\n", primerDataSlice)
primerKeys = primerData[0::2]
primerValues = primerData[1::2]
primerDict = {"Primer Data": dict(zip(primerKeys,primerValues))}
# Generatring the JSON array "Primer Data"
primerJSON = json.dumps(primerDict, ensure_ascii=False)
file.write(primerJSON)
# Grabbing the date (this has just the date, so json will have to add date.)
date = re.findall('(\d{2}[\/\- ](\d{2}|january|jan|february|feb|march|mar|april|apr|may|may|june|jun|july|jul|august|aug|september|sep|october|oct|november|nov|december|dec)[\/\- ]\d{2,4})', clean2)
Without input data it is difficult to give you working code. A minimal working example with input would help. As for JSON handling, python dictionaries can dump to json easily. See examples here.
https://docs.python-guide.org/scenarios/json/
Get a json string from a dictionary and write to a file. Figure out how to parse the text into a dictionary.
import json
d = {"Date" : "21feb2019", "Sequence ID" : "lacz-rp", "Sequence 5'-3'" : "gat"}
json_data = json.dumps(d)
print(json_data)
# Write that data to a file
So, I did figure this out, the problem was really just that because of the way my pre-processing was pulling all the data into a single list wasn't really that great of an idea considering that the keys for the dictionary never changed.
Here is the semi-finished result for making the Dictionary and JSON file.
# Collect the sequence name
name = clean2[clean2.index("Sequence") + 11: clean2.index("Sequence") + 19]
# Collecting Shipment info
ordered = input("Who placed this order? ")
received = input("Who is receiving this order? ")
dateOrder = re.findall(
r"(\d{2}[/\- ](\d{2}|January|Jan|February|Feb|March|Mar|April|Apr|May|June|Jun|July|Jul|August|Aug|September|Sep|October|Oct|November|Nov|December|Dec)[/\- ]\d{2,4})",
clean2)
dateReceived = date.today()
refNo = clean2[clean2.index("ref.No. ") + 8: clean2.index("ref.No.") + 17]
orderNo = clean2[clean2.index("Order No.") +
10: clean2.index("Order No.") + 18]
# Finding and grabbing the sequence data. Storing it and then finding the
# GC content and melting temp or TM
bases = int(clean2[clean2.index("bases") - 3:clean2.index("bases") - 1])
seqList = [line for line in clean2 if re.match(r'^[AGCT]+$', line)]
sequence = "".join(i for i in seqList[:bases])
def gc_content(x):
count = 0
for i in x:
if i == 'G' or i == 'C':
count += 1
else:
count = count
return round((count / bases) * 100, 1)
gc = gc_content(sequence)
tm = mt.Tm_GC(sequence, Na=50)
moleWeight = round(mw(Seq(sequence, generic_dna)), 2)
dilWeight = float(clean2[clean2.index("ug/OD260:") +
10: clean2.index("ug/OD260:") + 14])
dilution = dilWeight * 10
primerDict = {"Primer Data": {
"Sequence": sequence,
"Bases": bases,
"TM (50mM NaCl)": tm,
"% GC content": gc,
"Molecular weight": moleWeight,
"ug/0D260": dilWeight,
"Dilution volume (uL)": dilution
},
"Shipment Info": {
"Ref. No.": refNo,
"Order No.": orderNo,
"Ordered by": ordered,
"Date of Order": dateOrder,
"Received By": received,
"Date Received": str(dateReceived.strftime("%d-%b-%Y"))
}}
# Generating the JSON array "Primer Data"
with open("".join(name) + ".json", 'w') as file:
primerJSON = json.dumps(primerDict, ensure_ascii=False)
file.write(primerJSON)

Expand selection to custom line

I am wondering if there is a out of the box way, or a plugin that can achieve the following behaviour in SublimeText3.
I would like to put the caret at a certain line. And then select all the text until another line number. The amount of lines should be variable.
For example put the caret on 10 and then expand selection to line 21 or line 104.
I hate having to hold down key or use the mouse for this action.
I wrote a simple plugin that allows you to enter a line to select until via an input_panel:
Features:
works bidirectionally
respects the current selection
only executes if there is a single selection
Setup Info:
# GitHub
Code:
import sublime, sublime_plugin
class SelectToLineCommand( sublime_plugin.TextCommand ):
def run( self, edit ):
window = self.view.window()
selections = self.view.sel()
if len( selections ) != 1:
return
self.currentSelection = selections[0]
if self.currentSelection.a > self.currentSelection.b:
self.currentSelection = sublime.Region( self.currentSelection.b, self.currentSelection.a )
window.show_input_panel( "Select To Line Number", "", self.get_LineNumber, None, None )
def get_LineNumber( self, userInput ):
lineToRow_Offset = 1
row = int( userInput ) - lineToRow_Offset
selectionEnd_Row = self.view.text_point( row, 0 )
currentSelection = self.currentSelection
if selectionEnd_Row >= currentSelection.b:
selectionStart = currentSelection.a
selectionEnd = self.view.line( selectionEnd_Row ).b
elif selectionEnd_Row < currentSelection.a:
selectionStart = currentSelection.b
selectionEnd = self.view.line( selectionEnd_Row ).a
newSelection = sublime.Region( selectionStart, selectionEnd )
self.view.selection.clear()
self.view.selection.add( newSelection )

Python csv.reader to separate items by comma but ignore those within pairs of double-quotes

I'm trying to use csv.reader to create a list of items from a string, but I'm having trouble. For instance, I have the following string:
bibinfo = "wooldridge1999asymptotic, author = \"Wooldridge, Jeffrey M.\", title = \"Asymptotic Properties of Weighted M-Estimators for Variable Probability Samples\", journal = \"Econometrica\", volume = \"\", year = 1999"
And I run the following code:
import csv
from io import StringIO
bibitems = [bibitem for bibitem in csv.reader(StringIO(bibinfo), skipinitialspace = True)][0]
But instead of having a list in which commas within a pair of double-quotes are not considered as separators, I obtain the following (unwanted) result:
['wooldridge1999asymptotic', 'author = "Wooldridge', 'Jeffrey M."', 'title = "Asymptotic Properties of Weighted M-Estimators for Variable Probability Samples"', 'journal = "Econometrica"', 'volume = ""', 'year = 1999']
In other words, it separates some items (like author's surname from first name) when it should not. I followed the tips in this other link, but it seems that I'm missing something else too.
It works if the " is at beginning of the item:
"author = Wooldridge, Jeffrey M."
With the changed text:
>>> s = """wooldridge1999asymptotic, "author = Wooldridge, Jeffrey M.", title = "Asymptotic Properties of Weighted M-Estimators for Variable Probability Samples", journal = "Econometrica", volume = "", year = 1999"""
>>> list(csv.reader(s.splitlines(), skipinitialspace=True))
[['wooldridge1999asymptotic',
'author = Wooldridge, Jeffrey M.',
'title = "Asymptotic Properties of Weighted M-Estimators for Variable Probability Samples"',
'journal = "Econometrica"',
'volume = ""',
'year = 1999']]

MySql Database connection with python

I've got an issue trying to connect to a database with python. It compiles without error but it doesn't seem to do anything. I'm not sure if I'm instantiating the class incorrectly or what the issue may be. Could someone point me in the right direction?
import _mysql
import MySQLdb
class Operations:
def open():
db=_mysql.connect("127.0.0.1","root","admin","test")
c=db.cursor()
#deletes the cursor
def close(self):
c.close()
#verifies the credentials and advances
def login(self):
print "Welcome to the online bookstore login!"
x = raw_input('Please enter your user id. ')
y = raw_input('Please enter your password. ')
c.execute("""SELECT userid,password FROM members WHERE userid = %s""", (x,))
z = c.password
if y == z:
member_menu()
else:
close()
def new_user(self):
print "Welcome to the Online book store new user registration page!"
print "To begin, please enter your first name: "
fName = raw_input('Please enter your first name: ')
lName = raw_input('Please enter your last name: ')
address = raw_input('Please enter your street address: ')
city = raw_input('Please enter your city: ')
state = raw_input('Please enter your state: ')
zip_code = raw_input('Please enter your zip code: ')
phone = raw_input('Please enter your phone number: ')
email = raw_input('Please enter your email: ')
user_ID = raw_input('Please enter your user id: ')
password = raw_input('Please enter your password: ')
c.executemany("""INSERT INTO members(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s,) VALUES (fName, lName, address, city, state, zip_code, phone, email, user_id, password,)""")
print "Your account has been created. returning to the welcome menu. "
welcome()
def welcome(self):
choice = NONE;
print "**********************************************************************\n"
print "***\t\t\t\t\t\t\t\t ***\n"
print "***\t\tWelcome to the Online Book Store\t\t ***\n"
print "***\t\t\t\t\t\t\t\t ***\n"
print "**********************************************************************\n"
print "1. Member Login\n"
print "2. New Member Registration\n"
print "3. Quit\n"
choice = raw_input('Type in your option: ')
if choice == 1:
login()
elif x == 2:
new_user()
else:
close()
def member_menu(self):
x = NONE
print "**********************************************************************\n"
print "***\t\t\t\t\t\t\t\t ***\n"
print "***\t\t Welcome to the Online Book Store \t\t ***\n"
print "***\t\t\t Member Menu \t\t\t ***\n"
print "***\t\t\t\t\t\t\t\t ***\n"
print "**********************************************************************\n"
print "1. Search by Author/Title/Subject\n"
print "2. View/Edit Shopping Cart\n"
print "3. Check Order Status\n"
print "4. One Click Check Out\n"
print "5. Logout\n"
print "Type in your option: "
x = raw_input('Please enter your choice. ')
if x == 1:
close_conn(),
elif x == 2:
close_conn(),
elif x == 3:
close_conn(),
elif x == 4:
close_conn(),
else:
close_conn()
def main():
start = Operations()
print "Opening conenction to database"
start.welcome
if __name__ == '__main__':
main()
Well, there are so many problems with your code, that I'll probably miss some of them anyway.
Nothing happens, because your main() function and condition are both parts of the class definition, so all the interpreter sees are actually two imports and a class definition.
Let's say we unindented the main() definition and the condition. All that would happen then is creating an instance of Operations (with no special effects, as you have no custom constructor defined) and printing "Opening connection to database" to the screen, because all the last line in main() does is getting a reference to the welcome() method and ignoring it. You need to call it: start.welcome()
When you do call it, much more problems will appear. NameErrors will probably come first, as you are using identifiers that do not exist in given scopes. It seems you're new to Python's object model and probably coming from a language with a different approach, like C++. In Python all non-static and non-class instance methods take a reference to the object they're operating on as the first parameter, traditionally called 'self'. If you want to access any of the fields of the object, you need to do this through 'self', they are not visible to the interpreter otherwise. E.g.: you open a connection and keep the cursor as c, which you later reuse in other methods:
def open():
# ...
c=db.cursor()
# ...
def login(self):
# ...
c.execute("...")
That's incorrect for two reasons:
your open() method does not take self as a parameter
you're creating c as a local variable in scope of the open() method and then trying to access it in login(), which essentialy results in a "reference before assignment" error.
In order to be correct, it should be written like this:
def open(self):
# ...
self.c = db.cursor()
# ...
def login(self):
# ...
self.c.execute("...")
You're making the same mistake in many places. You need to call self.login(), self.new_user(), self.close(), etc.
You're using Python 2, at least according to the question's tags and there is one thing you need to remember when declaring classes in Python 2. There exist so called old- and new-style classes and what you want to do is use the new-style ones. Therefore your class must inherit from object:
class Operations(object):
# ...
They've finally decided to drop the old-style classes support in Python 3 and there's no need to explicitly inherit from object anymore, but while in Python 2, you need to cope with it.
While there are still some errors or potential errors (what is close_connection()?), I think it's enough for a good start ;). Good luck.