GtkListBox, how to prevent auto-selecting a row on window show - pygtk

I have a list box with several rows attached to a window.
list_box = Gtk.ListBox()
list_box.insert(Gtk.Label('foo'), -1)
list_box.insert(Gtk.Label('bar'), -1)
list_box.insert(Gtk.Label('qux'), -1) # ListBoxRow is added automatically
window = Gtk.Window()
window.add(list_box)
window.show_all()
When I call show_all(), the first row of the list is being selected automatically what I don't want to happen. How to prevent auto-selecting it?
I tried changing the order of the functions call
window.show_all()
window.add(list_box)
which broke the layout and the size of the window doesn't fit to the list.

I was running into this issue as well, I used the following code to do it:
listbox = Gtk.ListBox(margin=0)
listbox.set_selection_mode(Gtk.SelectionMode.NONE)
I can still click on each row and do a callback with the following, as well:
listbox.connect("row-activated", self.callback)

The ListBox has a property selection-mode, which you can set to Gtk.SELECTION_NONE. In this case none of the rows will be selected (and cannot be selected later). I don't know if that is what you want.
You can also call the method unselect_all, which will unselect all rows. For this to work, the ListBox must be in SELECT_MULTIPLE or SELECT_SINGLE mode.
This example seems to work completely as expected (i.e. no selection at the start, and if a line is selected, the button can unselect it). If in your installation it doesn't work, I would try to update your packages:
from gi.repository import Gtk
class MainWindow(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.connect("delete-event", self.on_delete)
self.listbox = Gtk.ListBox()
self.listbox.insert(Gtk.Label('foo'), -1)
self.listbox.insert(Gtk.Label('bar'), -1)
self.listbox.insert(Gtk.Label('qux'), -1) # ListBoxRow is added automatically
button = Gtk.Button("Clear selection")
button.connect("clicked", self.on_button_clicked)
vbox = Gtk.VBox()
vbox.pack_start(button, False, True, 0)
vbox.pack_start(self.listbox, False, True, 0)
self.add(vbox)
self.show_all()
Gtk.main()
def on_button_clicked(self, btn):
self.listbox.unselect_all()
def on_delete(self, win, event):
Gtk.main_quit()
def main():
w = MainWindow()
return 0
if __name__ == '__main__':
main()
A note about gtk3 themes: Some themes do not show selected rows at all. Particularly dark themes such as eg. FlatStudioDark, but also some light themes.

Add a dummy label to the top of your list_box and hide it:
dummyLabel = Gtk.Label('nothing') # workaround for focus of title entry
list_box.insert(dummyLabel, -1)
list_box.insert(Gtk.Label('foo'), -1)
list_box.insert(Gtk.Label('bar'), -1)
list_box.insert(Gtk.Label('qux'), -1) # ListBoxRow is added automatically
window = Gtk.Window()
window.add(list_box)
window.show_all()
dummyLabel.hide()
Gtk.main()
The result:
result

Related

How do I delay the execution of a function within tkinter button command

I am trying to simulate a computer player "clicking" a button after a human user clicks a button In a grid of buttons. If I use the .after method the 'state' of the button change is delayed but it executes my check_state() method which doesn't detect the change. When I try time.sleep() method it prolongs the human click but still immediately invokes the 'auto' click regardless of where I put it in my code. I want a delay between the human click and 'auto' click.
I have tried widget.after(1000) which gives the desired delay of 'auto' click, but doesn't allow my the change to be seen by my check_state() function. I have tried time.sleep() which delays the execution of the human button click but the 'auto' click is still immediately invokes regardless of which order I place the sleep() function in relation to the call to auto_click(). I know there are better practices for this code implementation like using class based structure which I plan on using once my logic and functionality issues are resolved. My code is as follows:
import tkinter as tk
import random
def app():
def auto_click():
grid_state = get_grid_state()
possible_clicks = []
for i in range(0, len(grid_state)):
if grid_state[i] == " ":
possible_clicks.append(i)
#debug.config(text=possible_moves)
click = random.choice(possible_clicks)
buttons[click].after(1000, lambda: buttons[click].config(text = "auto", state=tk.DISABLED))
#time.sleep(1)
check_grid_state()
check_grid_full()
debug.config(text="test")
def onclick(*arg):
global is_full
buttons[arg[0]].config(text = "clicked", state=tk.DISABLED)
check_grid_state()
check_grid_full()
if not is_full:
auto_click()
def check_grid_full():
global is_full
result=[]
for i in range(len(buttons)):
result.append(buttons[i].cget('state'))
r = [*set(result)]
if r == ['disabled']:
is_full = True
grid_status.config(text=is_full)
else:
is_full = False
Retrieve the current state of the grid
def get_grid_state():
grid_state =[]
for i in range(len(buttons)):
grid_state.append(buttons[i].cget('text'))
return grid_state
Check grid state
def check_grid_state():
grid_states.config(text=get_grid_state())
Global Variables
is_full = False
buttons = []
c=0
Window
root = tk.Tk()
root.title("Title")
Heading
label = tk.Label(root, text="grid state", font = ("Ariel black",22, "bold"))
label.pack()
Grid Frame
frame = tk.Frame(root)
frame.pack()
for row in range(3):
for column in range(3):
buttons.append(tk.Button(frame, text=f" ", font=("arial", "22"), state=tk.ACTIVE, height=2, width=2, command=lambda c=c: onclick(c)))
buttons[c].grid(row=row, column=column)
c += 1
Status bar
grid_states = tk.Label(root, text=f"")
grid_states.pack()
grid_status = tk.Label(root, text=f"")
grid_status.pack()
#btn_is = tk.Label(root, text=f"")
#btn_is.pack()
Debugging output label
debug = tk.Label(root, text="debug")
debug.pack()
Event loop
root.mainloop()
if name == "main":
app()

Pysimplegui and Pygame merge

hi I am trying to merge a pygame program with pysimplegui.
i'm looking for a way of grabbing the downstroke of a keyboard key. at the moment i can only see an upstroke of the key no matter what im doing.
I have sucessfully created pysimplegui programs but this is the first time i'm trying to merge with pygame.
window is setup by
window = sg.Window('UNICHALL V2.0',
Win_layout,
border_depth =20,
resizable = True,
keep_on_top = False,
finalize=True,
return_keyboard_events=True,
use_default_focus=False)
and in my pygame program i use
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
stop = pygame.time.get_ticks()
delta = stop - start
key = event.key
if key == 27:
sys.exit(1) ...
i can't get these two work in harmony, they either internally loop forever or simply stop the program dead.
Any help appreciated.
If you just need to detect when key pressed and released, try to bind the event '<KeyPress>' and '<KeyRelease>', not set option return_keyboard_events=True of sg.Window.
Example Code
import PySimpleGUI as sg
layout = [
[sg.InputText('', key='-INPUT-')],
[sg.Button('Add Binding'), sg.Button('Remove Binding')],
]
window = sg.Window('Test Program', layout, finalize=True)
while True:
event, values = window.read()
if event == sg.WIN_CLOSED:
break
elif event in ('Press', 'Release'):
e = window.user_bind_event
action = 'pressed' if event == 'Press' else 'released'
print(f'char:{repr(e.char)} | keycode:{e.keycode} | keysym:{e.keysym} | keysym_num:{e.keysym_num} {action}')
elif event == 'Add Binding':
window.bind("<KeyPress>", "Press")
window.bind("<KeyRelease>", "Release")
elif event == 'Remove Binding':
window.TKroot.unbind("<KeyPress>")
window.user_bind_dict.pop("<KeyPress>", None)
window.TKroot.unbind("<KeyRelease>")
window.user_bind_dict.pop("<KeyRelease>", None)
window.close()

How to confirm if user has pressed on a button in pysimplegui

I want the Predict Risk button to work only if one of the above buttons have been pressed before.
The code for the ui is as follows:
layout=[[sg.Text('Choose the Industry')],
[sg.Button("Automobile"),sg.Button("Chemical"),sg.Button("Engineering/Consulting"),
sg.Button("FMCG"),sg.Button("Healthcare/Hospitality"),sg.Button("Infrastructue")],
[sg.Button("IT/Comm/DC"),sg.Button("Manufacturing"),sg.Button("Mines"),
sg.Button("Energy/Oil & Gas"),sg.Button("Pharma"),sg.Button("Retail"),sg.Button("Cement")],
[sg.Text(size=(50,1),key=('loaded'))],
[sg.Text('Enter Observation/Recommendation: ', size =(26, 1)), sg.InputText()],
[sg.Button("Predict Risk")],
[sg.Text(size=(30,1),key=('output'))],
[sg.Text('If the above prediction is correct select \'yes\' else select the correct risk.')],
[sg.Button("Yes"),sg.Button("Low"),sg.Button("Medium")],
#[sg.Text('Select the correct risk: '),sg.Button("Low"),sg.Button("Medium")],
[sg.Text(size=(30,2),key=('trained'))],
[sg.Button("Exit"),sg.Button("Clear Fields")]
]
window=sg.Window("Risk Predictor",layout)
while True:
event, values = window.read()
#obs=values[0]
# End program if user closes window or
if event == sg.WIN_CLOSED or event == 'Exit':
break
window.close()
The above code is not complete.
I want the rest of application to be active only when the one of the top buttons has been pressed.
Secondly is there a way to map the enter key with Predict Risk button so user can just hit enter and get the prediction.
You can use variables or attribute button.metadata to record the state clicked for buttons. Initial value for each button is set by option metadata=False in sg.Button means not yet clicked. When button clicked, button.metadata will be set to True, it means this button clicked.
Here show the way to record the clicked state in button.metadata.
import PySimpleGUI as sg
items = [
"Automobile", "Chemical", "Engineering/Consulting", "FMCG",
"Healthcare/Hospitality", "Infrastructue", "IT/Comm/DC", "Manufacturing",
"Mines", "Energy/Oil & Gas", "Pharma", "Retail", "Cement",
]
length = len(items)
size = (max(map(len, items)), 1)
sg.theme("DarkBlue3")
sg.set_options(font=("Courier New", 11))
column_layout = []
line = []
num = 4
for i, item in enumerate(items):
line.append(sg.Button(item, size=size, metadata=False))
if i%num == num-1 or i==length-1:
column_layout.append(line)
line = []
layout = [
[sg.Text('Choose the Industry')],
[sg.Column(column_layout)],
[sg.Text(size=(50,1),key=('loaded'))],
[sg.Text('Enter Observation/Recommendation: ', size =(26, 1)), sg.InputText()],
[sg.Button("Predict Risk", bind_return_key=True)],
[sg.Text(size=(30,1),key=('output'))],
[sg.Text('If the above prediction is correct select \'yes\' else select the correct risk.')],
[sg.Button("Yes"),sg.Button("Low"),sg.Button("Medium")],
[sg.Text(size=(30,2),key=('trained'))],
[sg.Button("Exit"),sg.Button("Clear Fields")]
]
window=sg.Window("Risk Predictor", layout, use_default_focus=False, finalize=True)
for key in window.key_dict: # Remove dash box of all Buttons
element = window[key]
if isinstance(element, sg.Button):
element.block_focus()
while True:
event, values = window.read()
if event in (sg.WIN_CLOSED, 'Exit'):
break
elif event in items:
window[event].metadata = True
elif event == "Predict Risk" and window["Mines"].metadata:
print("Predict Risk for Mines")
window.close()

Python 3.x tkinter - can't get checkbox' variable value

Please help! As it says in the title - I can't get checkbox' variable value.
def init_widgets(self):
ttk.Button(self.root, command=self.insert_txt, text='Button', width='10').place(x=10, y=10)
...
cbcc = ttk.Checkbutton(root, text="Damage", onvalue="on", offvalue='off').place(x=235, y=12)
...
def insert_txt(self):
...
cbcd = StringVar()
cbcd.get()
print(cbcd)
if cbcd == "on":
self.damage
else:
Print delivers "PY_VAR2" and counting up from there with every time I click (PY_VAR3, etc.)
It seems in your code, the argument 'variable' in your Checkbutton is not bounded, furthermore to get the value from IntVar or StringVar you would use (IntVar.get(), StringVar().get()), you could use the next code as example to use Checkbutton widget.
'''
CheckBox Test
References:
http://effbot.org/tkinterbook/checkbutton.htm
'''
from tkinter import *
class App:
def __init__(self, master):
self.var = IntVar() #This is the variable bounded to checkbutton to
#get the checkbutton state value
#
frame = Frame(master)
frame.pack()
self.checkbutton = Checkbutton(frame, text="Hello Checkbutton",
command=self.say_hello, variable=self.var)
self.checkbutton.pack(side=LEFT)
#
self.button = Button(frame, text="QUIT", fg="red", command=frame.quit)
self.button.pack(side=LEFT)
def say_hello(self):
'''
Function Bounded to Checkbutton in command parameter, every click
either check or un-check print the current state of the checkbutton
'''
print("State Changed:", self.var.get())
if __name__ == '__main__':
root = Tk()
app = App(root)
#
root.mainloop()
root.destroy() # Quit the App when you click "Quit"
Every click on the check button, you will see printed in the console the value of the current state for the checkbutton. You can check the next reference to get an general overview tkinter widgets: http://effbot.org/tkinterbook/checkbutton.htm
I hope this snippet helps you.
Very Best Regards.

tkinter variable for drop down selection empty

I tried to program an app in tkinter that would load random lines from a file you select from a pull down menu and display the selected line in a text window.
It seems like the variable "var" in insert_text does not return the selected "option" but rather an "empty" string resulting in a the following error:
"File not found error" (FileNotFoundError: [Errno2] No such file or
directory: '').
Please help!
#!/usr/bin/env python
# Python 3
import tkinter
from tkinter import ttk
import random
class Application:
def __init__(self, root):
self.root = root
self.root.title('Random Stuff')
ttk.Frame(self.root, width=450, height=185).pack()
self.init_widgets()
var = tkinter.StringVar(root)
script = var.get()
choices = ['option1', 'option2', 'option3']
option = tkinter.OptionMenu(root, var, *choices)
option.pack(side='right', padx=10, pady=10)
def init_widgets(self):
ttk.Button(self.root, command=self.insert_txt, text='Button', width='10').place(x=10, y=10)
self.txt = tkinter.Text(self.root, width='45', height='5')
self.txt.place(x=10, y=50)
def insert_txt(self):
var = tkinter.StringVar(root)
name = var.get()
line = random.choice(open(str(name)).readlines())
self.txt.insert(tkinter.INSERT, line)
if __name__ == '__main__':
root = tkinter.Tk()
Application(root)
root.mainloop()
That's because you're just creating an empty StringVar that isn't modified later, thus returning an empty string.
The OptionMenu takes the command parameter that calls the specified method every time another option is selected. Now, you can call a method like this, replacing you insert_txt:
def __init__(self):
# ...
self.var = tkinter.StringVar()
self.options = tkinter.OptionMenu(root, var, *choices, command=self.option_selected)
# ...
def option_selected(self, event):
name = self.var.get()
# The stuff you already had
Additionally, you have to empty the Text widget, otherwise the previous text would stay. I think the Entry widget is better for that, too.