GTK how to create 3 different filter for 1 Liststore - filter

In GTK/Python, I'm trying to build an interface with nodes.
This is photo of my interface
I create one liststore and I want to filter different things. The user has to do two actions, first, he has to choose in the combobox the type of filter that he wants to use, if he wants to filter by node's type, node's beginning name or others.
Then in the text entry, he decides what information that he wants to see. Take the exemple of nodes type. There are like 3 different types of nodes, node type 1, node type 2 and node type 3. As an user I want to see only node type 1, so I write 1 in the node entry. Actually I have a problem, it is my filter does not work.
I first create 2 liststores:
def create_liststore(self):
if len(self.FdessinCarto.pos) != 0:
for i,node in enumerate(self.FdessinCarto.pos):
self.node_liststore.append([str(node.title),self.controller.model.string_from_numdate(int(node.start_time)),self.controller.model.string_from_numdate(int(node.end_time)),str(node.node_group),str(node.description),str(node.attachment_list)])
self.edgelist = self.controller.get_edge_list()
if len(self.edgelist) !=0:
for i in self.edgelist:
edge_prop=self.controller.edge_data(i[0],i[1])
self.edge_liststore.append([edge_prop['label'],str(i[0].title),str(i[1].title),edge_prop['description'],edge_prop['attachment_list']])
#creating the treeview for Node, making it use the filter as a model, and adding the columns
self.treeviewNode = Gtk.TreeView.new_with_model(self.node_liststore)
for i, column_title in enumerate(["Name", "Beginning date", "End date", "Type of node", "Description of node","files"]):
self.Noderenderer = Gtk.CellRendererText()
self.Noderenderer.set_property("editable", True)
column = Gtk.TreeViewColumn(column_title, self.Noderenderer, text=i)
column.set_sort_column_id(0)
self.treeviewNode.append_column(column)
#creating the treeview for edge
self.treeviewEdge = Gtk.TreeView.new_with_model(self.edge_liststore)
for i, column_title in enumerate(["Name", "Node 1", "Node 2", "Description of edge","file"]):
self.Edgerenderer = Gtk.CellRendererText()
self.Edgerenderer.set_property("editable", True)
column = Gtk.TreeViewColumn(column_title, self.Edgerenderer, text=i)
column.set_sort_column_id(0)
self.treeviewEdge.append_column(column)
self.SWViewListStore.add(self.treeviewNode)
self.SWEdgeStore.add(self.treeviewEdge)
self.SWViewListStore.show_all()
self.SWEdgeStore.show_all()
There are my 3 different filters:
#creating the filtre
self.node_beginning_date_filter = self.node_liststore.filter_new()
self.node_end_date_filter = self.node_liststore.filter_new()
self.node_type_filter = self.node_liststore.filter_new()
#setting the filter function, note that we're not using the
self.node_end_date_filter.set_visible_func(self.node_end_date_filter_func)
self.node_beginning_date_filter.set_visible_func(self.node_beginning_date_filter_func)
self.node_type_filter.set_visible_func(self.node_type_filter_func)
Once I change my combo-box, it would activate my function, it took the type of combofilter then also the text of combobox :
def on_entryComboBox_changed(self,widget):
textComboFilter = self.View.combo_filter.get_active_text()
print("textComboFilter %s" %textComboFilter)
if textComboFilter == "Filter by node's beginning date":
#print("%s language selected!" % textComboFilter)
self.View.current_filter = textComboFilter
self.View.node_beginning_date_filter.refilter()
if textComboFilter == "Filter by node's end date":
#print("%s language selected!" % textComboFilter)
self.View.current_filter = textComboFilter
self.View.node_end_date_filter.refilter()
if textComboFilter == "Filter by type of node":
#print("%s language selected!" % textComboFilter)
self.View.current_filter = textComboFilter
self.View.node_type_filter.refilter()
And it does not work.

Finally,
I manage to answer it on my own.
You should build one filter rather than three. For people who have the same problem, this is a very good example which helps me to solve my problem.
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
#list of tuples for each software, containing the software name, initial release, and main programming languages used
software_list = [("Firefox", 2002, "C++"),
("Eclipse", 2004, "Java" ),
("Pitivi", 2004, "Python"),
("Netbeans", 1996, "Java"),
("Chrome", 2008, "C++"),
("Filezilla", 2001, "C++"),
("Bazaar", 2005, "Python"),
("Git", 2005, "C"),
("Linux Kernel", 1991, "C"),
("GCC", 1987, "C"),
("Frostwire", 2004, "Java")]
class TreeViewFilterWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self, title="Treeview Filter Demo")
self.set_border_width(10)
#Setting up the self.grid in which the elements are to be positionned
self.grid = Gtk.Grid()
self.grid.set_column_homogeneous(True)
self.grid.set_row_homogeneous(True)
self.add(self.grid)
#Creating the ListStore model
self.software_liststore = Gtk.ListStore(str, int, str)
for software_ref in software_list:
self.software_liststore.append(list(software_ref))
self.current_filter_language = None
#Creating the filter, feeding it with the liststore model
self.language_filter = self.software_liststore.filter_new()
#setting the filter function, note that we're not using the
self.language_filter.set_visible_func(self.language_filter_func)
#creating the treeview, making it use the filter as a model, and adding the columns
self.treeview = Gtk.TreeView.new_with_model(self.language_filter)
for i, column_title in enumerate(["Software", "Release Year", "Programming Language"]):
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(column_title, renderer, text=i)
self.treeview.append_column(column)
#creating buttons to filter by programming language, and setting up their events
self.buttons = list()
for prog_language in ["Java", "C", "C++", "Python", "None"]:
button = Gtk.Button(prog_language)
self.buttons.append(button)
button.connect("clicked", self.on_selection_button_clicked)
#setting up the layout, putting the treeview in a scrollwindow, and the buttons in a row
self.scrollable_treelist = Gtk.ScrolledWindow()
self.scrollable_treelist.set_vexpand(True)
self.grid.attach(self.scrollable_treelist, 0, 0, 8, 10)
self.grid.attach_next_to(self.buttons[0], self.scrollable_treelist, Gtk.PositionType.BOTTOM, 1, 1)
for i, button in enumerate(self.buttons[1:]):
self.grid.attach_next_to(button, self.buttons[i], Gtk.PositionType.RIGHT, 1, 1)
self.scrollable_treelist.add(self.treeview)
self.show_all()
def language_filter_func(self, model, iter, data):
"""Tests if the language in the row is the one in the filter"""
if self.current_filter_language is None or self.current_filter_language == "None":
return True
else:
return model[iter][2] == self.current_filter_language
def on_selection_button_clicked(self, widget):
"""Called on any of the button clicks"""
#we set the current language filter to the button's label
self.current_filter_language = widget.get_label()
print("%s language selected!" % self.current_filter_language)
#we update the filter, which updates in turn the view
self.language_filter.refilter()
win = TreeViewFilterWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()

Related

problems with the leaderboard discord.py

The leaderboard shows the same username even if they are different users in case they have the same value.
I don't know how to solve it but when in the code I ask to resist a variable it gives me only 3 elements and not 4 even if 4 come out.
code:
#client.command(aliases = ["lb"])
async def leaderboard(ctx,x = 10):
leader_board = {}
total = []
for user in economy_system:
name = int(user)
total_amount = economy_system[user]["wallet"] + economy_system[user]["bank"]
leader_board[total_amount] = name
total.append(total_amount)
print(leader_board)
total = sorted(total,reverse=True)
embed = discord.Embed(
title = f"Top {x} Richest People",
description = "This is decided on the basis of raw money in the bank and wallet",
color = 0x003399
)
index = 1
for amt in total:
id_ = leader_board[amt]
member = client.get_user(id_)
name = member.name
print(name)
embed.add_field(
name = f"{index}. {name}",
value = f"{amt}",
inline = False
)
if index == x:
break
else:
index += 1
await ctx.send(embed=embed)
print resists this:
{100: 523967502665908227, 350: 554617490806800387, 1100: 350886488235311126}
Padre Mapper
Flore (Orsolinismo)
Aetna
Aetna
In theory there should also be 100: 488826524791734275 (i.e. my user id) but it doesn't find it.
Your problem comes from this line:
leader_board[total_amount] = name
If total_amount is already a key (eg. two users have the same amount of money), it will replace the previous value (which was a user ID) and replace it with another user ID. In this situation, if multiple users have the same amount of money, only one will be saved in leader_board.
Then, you have this line:
total.append(total_amount)
In this case, if two users have the same amount of money, you would just have two identical values, which is normal but, considering the problem above, this will create a shift.
Let's say you have ten users with two of them who have the same amount of money. leader_board will only contain 9 items whereas total will contain 10 values. That's the reason why you have two of the same name in your message.
To solve the problem:
#client.command(aliases = ["lb"])
async def leaderboard(ctx, x=10):
d = {user_id: info["wallet"] + info["bank"] for user_id, info in economy_system.items()}
leaderboard = {user_id: amount for user_id, amount in sorted(d.items(), key=lambda item: item[1], reverse=True)}
embed = discord.Embed(
title = f"Top {x} Richest People",
description = "This is decided on the basis of raw money in the bank and wallet",
color = 0x003399
)
for index, infos in enumerate(leaderboard.items()):
user_id, amount = infos
member = client.get_user(user_id)
embed.add_field(
name = f"{index}. {member.display_name}",
value = f"{amount}",
inline = False
)
await ctx.send(embed=embed)
If I guessed right and your dictionnary is organized like this, it should work:
economy_system = {
user_id: {"bank": x, "wallet": y}
}

How to update bokeh active interaction with GeoJSON as data source?

I have made an interactive choropleth map with bokeh, and I'm trying to add active interactions using the dropdown widget (Select). However, most tutorials and SO questions about active interactions use ColumnDataSource, and not GeoJSONDataSource.
The issue is that GeoJSONDataSource doesn't have a .data method like ColumnDataSource does, so idk exactly how the syntax works when updating it.
My dataset is a dictionary in the form of city_dict = {'Amsterdam': <some data frame>, 'Antwerp': <some data frame>, ...}, where the dataframe is in geojson format. I have already confirmed that this format works when making glyphs.
def update(attr, old, new):
s_value = dropdown.value
p.title.text = '%s', s_value
new_src1 = make_dataset(s_value)
val1 = GeoJSONDataSource(new_src1)
r1.data_source = val1
where make_dataset is a function that transforms my original dataset into a dataset that can feed into the GeoJSONDataSource function. make_dataset requires a string (name of the city) to work eg. 'Amsterdam'. It works on passive interactions.
The main plot code (removed unnecessary stuff) is:
dropdown = Select(value='Amsterdam', options = cities)
controls = WidgetBox(dropdown)
initial_city = 'Amsterdam'
a = make_dataset(initial_city)
src1 = GeoJSONDataSource(a)
p = figure(title = 'Amsterdam', plot_height = 750 , plot_width = 900, toolbar_location = 'right')
r1 = p.patches('xs','ys', source = src1, fill_color = {'field' :'norm', 'transform' : color_mapper})
dropdown.on_change('value', update)
layout = row(controls, p)
curdoc().add_root(layout)
I've added the error I get. error handling message Message 'PATCH-DOC' (revision 1) content: {'events': [{'kind': 'ModelChanged', 'model': {'type': 'Select', 'id': '1147'}, 'attr': 'value', 'new': 'Antwerp'}], 'references': []}: ValueError("expected a value of type str, got ('%s', 'Antwerp') of type tuple",)

GtkTreeView filtering and selecting

I have a simple GtkTreeView and a GtkEntry used to filter the model.
When I type somehing into the entry, software_list is filtered by language.
software_list = [("Firefox", 2002, "C++"),
("Eclipse", 2004, "Java" ),
("Netbeans", 1996, "Java"),
("Chrome", 2008, "C++"),
("GCC", 1987, "C"),
("Frostwire", 2004, "Java")]
class TreeViewFilterWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.curr_filter = ''
self.entry = Gtk.Entry()
self.entry.connect('changed', self.on_text_change)
self.software_liststore = Gtk.ListStore(str, int, str)
for software_ref in software_list:
self.software_liststore.append(list(software_ref))
self.filter = self.software_liststore.filter_new()
self.filter.set_visible_func(self.filter_func)
self.treeview = Gtk.TreeView.new_with_model(self.filter)
for i, column_title in enumerate(["Software", "Release Year", "Programming Language"]):
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(column_title, renderer, text=i)
self.treeview.append_column(column)
self.treeview.get_selection().connect('changed', self.on_row_select)
# packing into boxes, showing components, starting main loop goes here
def on_text_change(self, entry):
self.curr_filter = entry.get_text()
self.filter.refilter()
def filter_func(self, model, iter, data):
if self.curr_filter:
return re.search(re.escape(self.curr_filter), model[iter][2])
else:
return True
The problem is, when I select i.e. "Chrome" from the list and then type "Java" into the entry, then, obviously, "Chrome" gets hidden but selection changes to some other, random row. I'd prefer TreeView unselected hidden elements instead of changing the selection. How can I do this?
This just works as expected in Gtk2, but in Gtk3 you need to deselect the row if it disappears. The appropriate code is
class TreeViewFilterWindow(Gtk.Window):
def __init__(...):
...
self.selection = self.treeview.get_selection()
self.filter.connect('row-deleted', self.on_row_deleted)
def on_row_deleted(self, model, path):
if self.selection.path_is_selected(path):
GObject.idle_add(self.selection.unselect_path, path)
I found that calling self.selection.unselect_path(path) directly didn't seem to work for some reason, but deferring it with idle_add sorted it out.

Check OptionMenu selection and update GUI

I'm working on a class project and I'm trying to take it beyond the requirements a little here (I'm doing my own homework, just need help improving it!) so I want to update the GUI based on certain selections the user makes instead of just having all irrelevent options available all the time (requirements are to just present the options).
I'm still new to Python and even more new to Tkinter so my only attempt has been the following:
#Step Type
ttk.Label(mainframe, text = "Step Type").grid(column = 1, row = 16)
type_entry = OptionMenu(mainframe, StepType, "Kill", "Explore" , "Conversation")
type_entry.grid(column = 2, row = 16, sticky = (E))
#Step Goal
if StepType.get() == "Kill":
ttk.Label(mainframe, text = "Required Kills").grid(column = 1, row = 17)
goal_entry = ttk.Entry(mainframe, width = 20, textvariable = StepGoal)
goal_entry.grid(column = 2, row = 17, sticky = (E))
elif StepType.get() == "Explore":
ttk.Label(mainframe, text = "Location ID").grid(column = 1, row = 17)
goal_entry = ttk.Entry(mainframe, width = 20, textvariable = StepGoal)
goal_entry.grid(column = 2, row = 17, sticky = (E))
elif StepType.get() == "Conversation":
ttk.Label(mainframe, text = "NPC ID").grid(column = 1, row = 17)
goal_entry = ttk.Entry(mainframe, width = 20, textvariable = StepGoal)
goal_entry.grid(column = 2, row = 17, sticky = (E))
Obviously what I want to do here is when the user selects one of the options from the menu, to display the corresponding entry box and label instead of having all 3 all the time.
Also looking for the same situation for CheckButton
Full working example: tested od 2.7.5 and 3.3.2
It use command= in OptionMenu to call function when user changed option.
import tkinter as ttk
#----------------------------------------------------------------------
def on_option_change(event):
selected = step_type.get()
if selected == "Kill":
goal_label['text'] = "Required Kills"
elif selected == "Explore":
goal_label['text'] = "Location ID"
elif selected == "Conversation":
goal_label['text'] = "NPC ID"
# show label and entry
#goal_label.grid(column=1, row=17)
#goal_entry.grid(column=2, row=17, sticky='E')
#----------------------------------------------------------------------
mainframe = ttk.Tk()
# Step Type
step_type = ttk.StringVar() # there is the rule: variable name lowercase with _
ttk.Label(mainframe, text="Step Type").grid(column=1, row=16)
type_entry = ttk.OptionMenu(mainframe, step_type, "Kill", "Explore" , "Conversation", command=on_option_change)
type_entry.grid(column=2, row=16, sticky='E')
step_type.set("Kill")
# Step Goal
step_goal = ttk.StringVar()
goal_label = ttk.Label(mainframe, text="Required Kills")
goal_label.grid(column=1, row=17)
goal_entry = ttk.Entry(mainframe, width=20, textvariable=step_goal)
goal_entry.grid(column=2, row=17, sticky='E')
# hide label and entry
#goal_label.grid_forget()
#goal_entry.grid_forget()
# --- star the engine ---
mainframe.mainloop()
BTW: you can use grid() and grid_forget() to show and hide elements.
EDIT: example with Radiobutton using trace on StringVar
import tkinter as ttk
#----------------------------------------------------------------------
def on_variable_change(a,b,c): # `trace` send 3 argument to `on_variable_change`
#print(a, b, c)
selected = step_type.get()
if selected == "Kill":
goal_label['text'] = "Required Kills"
elif selected == "Explore":
goal_label['text'] = "Location ID"
elif selected == "Conversation":
goal_label['text'] = "NPC ID"
#----------------------------------------------------------------------
mainframe = ttk.Tk()
# Step Type
step_type = ttk.StringVar() # there is the rule: variable name lowercase with _
ttk.Label(mainframe, text="Step Type").grid(column=1, row=16)
ttk.Radiobutton(mainframe, text="Kill", value="Kill", variable=step_type).grid(column=2, row=16, sticky='E')
ttk.Radiobutton(mainframe, text="Explore", value="Explore", variable=step_type).grid(column=3, row=16, sticky='E')
ttk.Radiobutton(mainframe, text="Conversation", value="Conversation", variable=step_type).grid(column=4, row=16, sticky='E')
step_type.set("Kill")
# use `trace` after `set` because `on_variable_change` use `goal_label` which is not created yet.
step_type.trace("w", on_variable_change)
# Step Goal
step_goal = ttk.StringVar()
goal_label = ttk.Label(mainframe, text="Required Kills")
goal_label.grid(column=1, row=17)
goal_entry = ttk.Entry(mainframe, width=20, textvariable=step_goal)
goal_entry.grid(column=2, row=17, sticky='E')
# --- star the engine ---
mainframe.mainloop()

Positional Argument Undefined

I am working on a larger project to write a code so the user can play Connect 4 against the computer. Right now, the user can choose whether or not to go first and the board is drawn. While truing to make sure that the user can only enter legal moves, I have run into a problem where my function legal_moves() takes 1 positional argument, and 0 are given, but I do not understand what I need to do to male everything agree.
#connect 4
#using my own formating
import random
#define global variables
X = "X"
O = "O"
EMPTY = "_"
TIE = "TIE"
NUM_ROWS = 6
NUM_COLS = 8
def display_instruct():
"""Display game instructions."""
print(
"""
Welcome to the second greatest intellectual challenge of all time: Connect4.
This will be a showdown between your human brain and my silicon processor.
You will make your move known by entering a column number, 1 - 7. Your move
(if that column isn't already filled) will move to the lowest available position.
Prepare yourself, human. May the Schwartz be with you! \n
"""
)
def ask_yes_no(question):
"""Ask a yes or no question."""
response = None
while response not in ("y", "n"):
response = input(question).lower()
return response
def ask_number(question,low,high):
"""Ask for a number within range."""
#using range in Python sense-i.e., to ask for
#a number between 1 and 7, call ask_number with low=1, high=8
low=1
high=NUM_COLS
response = None
while response not in range (low,high):
response=int(input(question))
return response
def pieces():
"""Determine if player or computer goes first."""
go_first = ask_yes_no("Do you require the first move? (y/n): ")
if go_first == "y":
print("\nThen take the first move. You will need it.")
human = X
computer = O
else:
print("\nYour bravery will be your undoing... I will go first.")
computer = X
human = O
return computer, human
def new_board():
board = []
for x in range (NUM_COLS):
board.append([" "]*NUM_ROWS)
return board
def display_board(board):
"""Display game board on screen."""
for r in range(NUM_ROWS):
print_row(board,r)
print("\n")
def print_row(board, num):
"""Print specified row from current board"""
this_row = board[num]
print("\n\t| ", this_row[num], "|", this_row[num], "|", this_row[num], "|", this_row[num], "|", this_row[num], "|", this_row[num], "|", this_row[num],"|")
print("\t", "|---|---|---|---|---|---|---|")
# everything works up to here!
def legal_moves(board):
"""Create list of column numbers where a player can drop piece"""
legals = []
if move < NUM_COLS: # make sure this is a legal column
for r in range(NUM_ROWS):
legals.append(board[move])
return legals #returns a list of legal columns
#in human_move function, move input must be in legal_moves list
print (legals)
def human_move(board,human):
"""Get human move"""
legals = legal_moves(board)
print("LEGALS:", legals)
move = None
while move not in legals:
move = ask_number("Which column will you move to? (1-7):", 1, NUM_COLS)
if move not in legals:
print("\nThat column is already full, nerdling. Choose another.\n")
print("Human moving to column", move)
return move #return the column number chosen by user
def get_move_row(turn,move):
move=ask_number("Which column would you like to drop a piece?")
for m in range (NUM_COLS):
place_piece(turn,move)
display_board()
def place_piece(turn,move):
if this_row[m[move]]==" ":
this_row.append[m[move]]=turn
display_instruct()
computer,human=pieces()
board=new_board()
display_board(board)
move= int(input("Move?"))
legal_moves()
print ("Human:", human, "\nComputer:", computer)
Right down the bottom of the script, you call:
move= int(input("Move?"))
legal_moves()
# ^ no arguments
This does not supply the necessary board argument, hence the error message.

Resources