Making a list of mouse over event functions in Tkinter - python

I'm making a GUI for a medical tool as a class project. Given a condition, it should output a bunch of treatment options gathered from different websites like webMD. I would like to be able to handle mouseover events on any of the treatments listed to give a little more information about the treatment (such as the category of drug, whether it is a generic or not, etc).
The labels are stored in a list, as I have no idea how many different treatments will be returned beforehand. So my question is how can I make these mouseover events work. I can't write a function definition for every single possible label, they would number in the hundreds or thousands. I'm sure there's a very pythonic way to do it, but I have no idea what.
Here's my code for creating the labels:
def search_click():
"""
Builds the search results after the search button has been clicked
"""
self.output_frame.destroy() # Delete old results
build_output() # Rebuild output frames
treament_list = mockUpScript.queryConditions(self.condition_entry.get()) # Get treatment data
labels = []
frames = [self.onceFrame, self.twiceFrame, self.threeFrame, self.fourFrame] # holds the list of frames
for treament in treament_list: # For each treatment in the list
label = ttk.Label(frames[treament[1] - 1], text=treament[0]) # Build the label for treatment
labels.append(label) # Add the treatment to the list
label.pack()
and here is what the GUI looks like (don't judge [-; )
The text "Hover over drugs for information" should be changed depending on which drug your mouse is hovering over.

I can't write a function definition for every single possible label, they would number in the hundreds or thousands. I'm sure there's a very pythonic way to do it, but I have no idea what.
Check out lambda functions which are nearly identical to what you want.
In your case, something like:
def update_bottom_scroll_bar(text):
# whatever you want to do to update the text at the bottom
for treatment in treament_list: # For each treatment in the list
label = ttk.Label(frames[treatment[1] - 1], text=treatment[0]) # Build the label for treatment
label.bind("<Enter>", lambda event, t=treatment: update_bottom_scroll_bar(text=t))
label.bind("<Leave>", lambda event: update_bottom_scroll_bar(text='Default label text'))
labels.append(label) # Add the treatment to the list
label.pack()
Also please spell your variables right, I corrected treament to treatment...

Related

Tkinter, How do i save the entry/dropdown selection to a str variable in my script

I have searched for answers to this question and always get sent back to printing the selection with print(var.get()).
I don't want to print the selection, but to store it in a variable that I can then use in my code.
For more context, I'm making a simple gui to filter data frames. There are a bunch of dropdown menus from which the user selects specific features. I then want to get all of those features to filter down my data frame to a single entry.
The code snippet below shows what I'm trying, but I don't know how to get the data frame that the function filter_df returns (the data frame df is defined before).
I'd also need to use the value of each previous dropdown menu to remove all the impossible feature values (as in, no entry has both the previous value and the one I select after).
Is any of this possible, or are there elements I'm going to have to find a way around ?
I thank any and all answers in advance, this website is what makes the (or at least my) coding world go round.
app = tk.Tk()
app.geometry('100x200')
detail_app_str = tk.StringVar(app)
detail_app_str.set('Select Scope')
detail_dropedown = tk.OptionMenu(app, detail_app_str, *feature_values)
detail_dropedown.config(width=90, font=('Helvetica', 12))
detail_dropedown.pack(side="top")
def filter_df(*args):
filtered_df = df[(df['A'] == int(detail_app_str.get()))]
return filtered_df
detail_app_str.trace_add("write", filter_df)

How to create n number of python functions based on user input?

Essentially, I'm looking to build a web app where the user can input n number of labels for a dataset put it into a dictionary with keywords for each label. I'd like the same function to be created for n number of labels, something like:
# labeling function for label 1
#labeling_function()
def lf_label_1(x):
if x.label_1 in ["bag", "surfboard", "skis"]:
return CARRY
return ABSTAIN
So, I'd get a new function for each new label added by the user. Each function then feeds into a list and ends up being input for a function. For example:
# list of labeling functions
lfs = [
lf_ride_object,
lf_carry_object,
lf_carry_subject,
lf_not_person,
lf_ydist,
lf_dist,
lf_area,
]
# applying label functions to create dataset
applier = PandasLFApplier(lfs)
L_train = applier.apply(df_train)
L_valid = applier.apply(df_valid)
For more details (and includes a second approach):
I'm looking for advice on how to use snorkel in a particular way. I'd like to create a labeling function with spaCy's PhraseMatcher. Basically, I want a user to input all of the words (with the corresponding label) in a web app and send it to the PhraseMatcher. Then, match a paragraph of text inside the labeling function.
How would I go about creating a labeling function(s?) for n number of labels on the backend? Typically, we would write code >n number of labeling functions for all the labels, but I'm trying to use snorkel in a use-case where we don't know how many labels there are until the user creates them.
Is there a way around this? Basically, the Matcher would go over the input text and check how many labels are in the text and then return all the labels found.Kind of like I'm trying to get the users to use snorkel without creating the functions themselves and only input the (label, word(s)) combinations.
Is there a way to use the Matcher in such a way that there is only one labeling function and it uses the Matcher for all labels?
For example, there could be a single labeling function that looks like this (peudo-code):
# labeling function for all labels
#nlp_labeling_function() # labeling function for using spaCy
def lf_labeler(x, label_keyword_dict):
labels = []
doc = nlp(x) # tokenizing the input text
matches = PhraseMatcher(doc) # finding all the token words that match a specific label
for match_id in matches:
labels.append(nlp.vocab.strings[match_id] # adds every label where there was a match
if labels: # runs if labels is not empty
return labels # this would return all the labels that were matches in the text document. Not sure if it's possible to return the list of labels like this.
else:
return ABSTAIN # abstains from using this text example in the dataset creation because there was no match
So, it would basically take in a piece of text (from a dataframe), check to see if any of the users' keywords are in the text and add all of the appropriate labels as a result. Return all the matched labels or simply return "ABSTAIN" to say that there were no matches.
While typing up this question I came up with some ideas that I wrote out here, so I'll be testing them in the meantime and have a look a the snorkel source code to see if I can come up with anything else.
Thanks!

Can we add annotations, or some kind of labels, to Folium Maps?

I have a basic SQL script which pulls data from MySQL , adds it to a dataframe, and then a Folium Map is created.
Here is my code.
#### login to DB
#### df = pd.read_sql("SELECT ... FROM ...)
m = folium.Map(location=[40.6976701, -74.2598704], zoom_start=10)
locations = list(zip(df.Latitude, df.Longitude))
#print(locations)
cluster = MarkerCluster(locations=locations)
m.add_child(cluster)
m
That produces this awesome map.
I can zoom in or zoom out, and the clusters expand or combine dynamically. Clearly the numbers are counts of items per cluster. I am wondering if I can add in another data point, like summing expenses per counts of items. So, in the image here, we can see a 3 at the top center. If that consists of 3 seperate expenses of 200 each, can I show the 600 as some kind of annotation, or label, pointing to the cluster? In the documentation I saw a parameters called popup and tooltip, but it doesn't seem to work for me.
Maybe I need to do some kind of aggregation, like this.
df.groupby(['Latitude','Longitude']).sum()
Just thinking out loud here.
I ended up doing this.
m = folium.Map(location=[40.6976701, -74.2598704], zoom_start=10)
for lat,lon,name,tip in zip(df.Latitude, df.Longitude, df.SiteName, df.Site):
folium.Marker(location=[lat,lon], tooltip = tip, popup = name)
m.add_child(cluster)
m
This lets you add a tooltip and a popup. That's pretty helpful. I still can't find a way to do sums. It seems like the only option is counts.

Coloring checkbuttons text in matplotlib

I need to add a checkbox to a python chart, to check the lines to be displayed or hidden.
I found the code below that works just fine, except one detail, that is annoyingly difficult for me to find out, I already have a few weeks since I am trying to do that: besides the checkboxes and the series names, I would like to also have the line colors along to the text, to be able to visually identify which line is which series.
Initially I tried to show the legend over the text, next to the checkboxes, but the legend would move if the window is resized, and that would not be feasible to implement.
My feeling is that there should be a way to add the line colors display within the function that creates the checkboxes, can anyone give me some suggestions on how to do that? The function is:
def func(label):
index = labels.index(label)
lines[index].set_visible(not lines[index].get_visible())
plt.draw()
The link with the full code is:
https://matplotlib.org/3.1.0/gallery/widgets/check_buttons.html
for idx, text in enumerate(check.labels):
text.set_color(lines[idx].get_color())
Add this bit of code after instantiating CheckButtons. It will color the text like you want it to.
Old, worse solution:
labels = [str(line.get_label()) + ", " + str(line.get_color()) for line in lines]

Pattern for associating pyparsing results with a linked-list of nodes

I have defined a pyparsing rule to parse this text into a syntax-tree...
TEXT COMMANDS:
add Iteration name = "Cisco 10M/half"
append Observation name = "packet loss 1"
assign Observation results_text = 0.0
assign Observation results_bool = True
append DataPoint
assign DataPoint metric = txpackets
assign DataPoint units = packets
append DataPoint
assign DataPoint metric = txpackets
assign DataPoint units = packets
append Observation name = "packet loss 2"
append DataPoint
assign DataPoint metric = txpackets
assign DataPoint units = packets
append DataPoint
assign DataPoint metric = txpackets
assign DataPoint units = packets
SYNTAX TREE:
['add', 'Iteration', ['name', 'Cisco 10M/half']]
['append', 'Observation', ['name', 'packet loss 1']]
['assign', 'Observation', ['results_text', '0.0']]
['assign', 'Observation', ['results_bool', 'True']]
['append', 'DataPoint']
['assign', 'DataPoint', ['metric', 'txpackets']]
['assign', 'DataPoint', ['units', 'packets']]
...
I'm trying to associate all the nested key-value pairs in the syntax-tree above into a linked-list of objects... the heirarchy looks something like this (each word is a namedtuple... children in the heirarchy are on the parents' list of children):
Log: [
Iteration: [
Observation:
[DataPoint, DataPoint],
Observation:
[DataPoint, DataPoint]
]
]
The goal of all this is to build a generic test data-acquisition platform to drive the flow of tests against network gear, and record the results. After the data is in this format, the same data structure will be used to build a test report. To answer the question in the comments below, I chose a linked list because it seemed like the easiest way to sequentially dequeue the information when writing the report. However, I would rather not assign Iteration or Observation sequence numbers before finishing the tests... in case we find problems and insert more Observations in the course of conducting the test. My theory is that the position of each element in the list is sufficient, but I'm willing to change that if it's part of the problem.
The problem is that I'm getting lost trying to assign Key-Values to objects in the linked list after it's built. For instance, after I insert an Observation namedtuple into the first Iteration, I have trouble reliably handling the update of assign Observation results_bool = True in the example above.
Is there a generalized design pattern to handle this situation? I have googled this for while, but I can't seem to make the link between parsing the text (which I can do) and managing the data-heirarchy (the main problem). Hyperlinks or small demo code is fine... I just need pointers to get on the right track.
I am not aware of an actual design pattern for what you're looking for, but I have a great passion for the issue at hand. I work heavily with network devices and parsing and organizing the data is a large ongoing challenge for me.
It's clear that the problem is not parsing the data, but what you do with it afterwards. This is where you need to think about the meaning you are attaching to the data you have parsed. The nested-list method might work well for you if the objects containing the lists are also meaningful.
Namedtuples are great for quick-and-dirty class-ish behavior, but they fall flat when you need them to do anything outside of basic attribute access, especially considering that as tuples they are immutable. It seems to me that you'll want to replace certain namedtuple objects with full-blown classes. This way you can highly customize the behavior and methods available.
For example, you know that an Iteration will always contain 1 or more Observation objects which will then contain 1 or more DataPoint objects. If you can accurately describe the relationships, this sets you on the path to handling them.
I wound up using textfsm, which allows me to keep state between different lines while parsing the configuration file.

Categories