How to setstylesheet of widgets individually stored in list - pyqt

below is just a pseudo code what i am trying to do.
so there is a button which i press to add more buttons but i store button widget in a list, which i can access by its index number.
# initialize class: this is just a pseudo code of what i am trying to do
def __init__(self,parent=None):
self.ButtonWidgetList=list()
self.i=0
self.MakeButton=QtWidgets.QPushButton(self) #assume this button is displayed and works
self.MakeButton.clicked.connect(self.NextButton) # assume this button calls Function NextButton
def NextButton(self):
self.ButtonWidgetList[self.i]=QtWidgets.QPushButton()
self.i=self.i+1
How can i access individual buttons stored in list by .setStyleSheet as i need it to change its image on press for example:
self.ButtonWidgetList[self.i].setStyleSheet("QPushButton#ButtonWidgetList{\n"
"border-image:url(:/imageA)};\n"
"QPushButton#ButtonWidgetList:pressed{\n"
"border-image:url(:/imageB)};\n"
i hope my question is understandable.

Related

i always get same (last added) value, trying to get kivy's button's text, using python 3

i have a python3 script, and in a Kivy app i populate a Boxlayout with some Buttons, using a for loop to set Buttons texts from a list.
i would like to get the right Button's text clicking each Button, but i always get same one. the last one.
My little code:
dishes=['spaghetti','rice','fish','salad']
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
class app(App):
def build(self):
self.box=BoxLayout()
for dish in dishes:
self.box.add_widget(Button(text=dish,on_press=lambda *args:print(dish)))
return self.box
app().run()
This way you definied anonymous functions with same body print(dish) for every button. Function is evauated on every click. So all buttons will execute print(dish) where dish is variable. Whatever you assign to this variable all buttons will print this value. In your case last value assigned to variable dish is 'salad', that's why all buttons prints 'salad'. It may look strange, but in fact variable dish is available also outside for loop. Python do not destroy this variable when for loop ends. It's a Python behaviour.
To get what you need I suggest replace line:
self.box.add_widget(Button(text=dish,on_press=lambda *args:print(dish)))
with
self.box.add_widget(Button(text=dish,on_press=lambda self:print(self.text)))
So every function will get self as function parameter (self = Button instance you just clicked), then you are able to print button's text attribute.

How to have a screen that lists multiple results, each of which is a clickable link (on Tkinter)

I'm new to GUI's,
I made a basic search engine that finds documents that contain certain words and puts them in a list.
Now, I want this list of paths to be displayed on my GUI, one under the other (using \n I guess), all of them clickable and automatically opening the right document for you with something like:
os.startfile(path, 'open')
In the current version I am only displaying one result (the first in the list) and I'm doing it with a label as so:
my_label.config(text=path)
my_label.bind("<Button-1>", lambda e: os.startfile(path, 'open'))
I could just make more labels but then it's inefficient and also not dynamic (the one I envision would list all results and be scrollable for example).
Appreciate any help in this.
You can use a set of Label and bind right click to it, or a single Listbox to show your paths and bind on it.
from tkinter import *
import os
root = Tk()
lst = [f'path {i}' for i in range(1,6)]
def select(e):
path = e.widget.get(*e.widget.curselection())
os.startfile(path,'open')
lstbox = Listbox(root)
lstbox.pack(padx=10,pady=10)
for i in lst:
lstbox.insert('end',i)
lstbox.bind('<<ListboxSelect>>',select) # Or lstbox.bind('<Double-1>',select) for double click
root.mainloop()
lst is supposed to be replaced with your required list. Another viable approach is set of Buttons with command that opens the required path.

how can I create a menuBar using tkinter in python

I really hope that you understand the following question, because I wasn't sure how to ask this question so I hope you can help me, if not let me now
I want to make a program to practise my python programming skill. I want to make a store program where an employee could input items with the price, and where he could sell the items. I want to make it that one “page” is the items input an another “page” is the sell page. To so that I want to make a menu bar where the employee could press on the let's say input button and go to input items page, but when he wants to sell items he presses on the sell button and then he goes to the sell page but only the screen changes, not that there comes another tab/window.
I really hope I explained my problem good enough
Thanks in advance!!
Based on your comments to the question, it appears your main problem is that you don't know how to make a menubar, so I will address that part of the question.
Every top level window (instances of Tk and Toplevel) have a menu attribute that can be set to an instance of a Menu object. This menu can itself have dropdown menus, and that combination is what makes a menubar.
In order to make this work you must first create the menu, and then associate that menu with the window.
import tkinter as tk
root = tk.Tk()
self.menubar = tk.Menu()
root.configure(menu=self.menubar)
To create the sub-menus you must do something very similar. First, create a new Menu instance, then associate it with the menubar using add_cascade. Typically, the menu will be a child of the menubar. You use the add_command method to add items to the sub-menu.
In your case, you might want to create a "View" menu with items for switching between "Input" and "Sell". It would look something like this:
viewMenu = tk.Menu(self.menubar)
self.menubar.add_cascade(label="View", menu=viewMenu)
viewMenu.add_command(label="Input", command=self.switch_to_input)
viewMenu.add_command(label="Sell", command=self.switch_to_sell)
Example
Here is a complete working example. To keep it simple and on topic it doesn't actually switch pages. Instead, it just displays the "page" in a label.
import tkinter as tk
class MenuExample:
def __init__(self):
self.root = tk.Tk()
self.label = tk.Label(self.root, width=25)
self.label.pack(side="top", fill="both", expand=True, padx=20, pady=20)
self._create_menubar()
def _create_menubar(self):
# create the menubar
self.menubar = tk.Menu(self.root)
self.root.configure(menu=self.menubar)
# File menu
fileMenu = tk.Menu(self.menubar)
self.menubar.add_cascade(label="File", menu=fileMenu)
fileMenu.add_command(label="Exit", command=self.root.destroy)
# View menu
viewMenu = tk.Menu(self.menubar)
self.menubar.add_cascade(label="View", menu=viewMenu)
viewMenu.add_command(label="Input", command=self.switch_to_input)
viewMenu.add_command(label="Sell", command=self.switch_to_sell)
def switch_to_input(self):
# put the code to switch to the input page here...
self.label.configure(text="you clicked on View->Input")
def switch_to_sell(self):
# put the code to switch to the sell page here...
self.label.configure(text="you clicked on View->Sell")
app = MenuExample()
tk.mainloop()

Kivy RecycleView: Get indices of all data items that are currently in view

I need to get the indices of all data items that are currently in view in a Kivy RecycleView widget. I want to display many Image widgets for which I render a texture and apply it to them. To save memory I need to delete these textures if they are not in view any more. I tried using the Kivy RecycleView example and modify it for my needs:
class SelectableImage(RecycleDataViewBehavior, Image):
def refresh_view_attrs(self, rv, index, data):
#Catch and handle the view changes
#print in view
print(index)
#call initial function of image class
return super(SelectableImage, self).refresh_view_attrs(rv, index, data)
The problem here is that refresh_view_attrs() only fires when a new Image widget gets added to the view. Thus I can only know what that last Image is, but not if there are any other in view. For this I'd also need to know which widget disappeared from the view. Is there any function in the RecycleView widget that I can use to obtain such information? Maybe there is a function that gets called whenever the view changes?
Okay, I found three possible solutions for my issue:
1. Solution
Calculate the position of all the Image widgets yourself and compare it with the position of the scrollbar to get the widgets that are currently displayed.
This is quite hacky and the RecycleView already does this internally. Thus I'd save the unnecessary computing and avoid this approach.
2. Solution
Use the get_visible_view(index) function of the view_adapter property of a RecycleView. This returns the currently visible view associated with index. If it return None, the view associated with index is not visible right now. This is how it's called for example:
self.myRecycleView.view_adapter.get_visible_view(index)
You could loop through the entire length of your data list and check for each item (the index in the list) if it is currently displayed or not.
3. Solution
My favourite approach: Use get_view_index_at(pos) of the layout_manager property to check what view index is at the given coordinates. This way you could check which item is at the top of the RecycleLayout widget and which one is at the bottom. You need to use coordinate transformation though. Example:
#get position at top center of RecycleView (upper limit)
pos = self.myRecycleView.to_local(self.myRecycleView.center_x, self.myRecycleView.height)
#check which items collides with the given position
print(self.myRecycleView.layout_manager.get_view_index_at(pos))
I hope this clears some things up!

In Python3/tkinter is there a way to temporarily stop accepting clicks in a Treeview widget?

I have a GUI based in Python 3 and tkinter that has a big ttk.Treeview. I have defined methods for row selection (one click) and opening an advanced info panel (double-click). I need to ensure that, after being double-clicked, for the next one or two seconds, the Treeview state won't be changed by another click. Is it possible to deactivate Treeview mouse bindings, like what we do with buttons?
Doing a little more research, I was able to come up with a solution for this. I just created an empty method that is called when the tree widget is supposed to be inactive. So, we can use something like this to "unbind" all the mouse events and re-bind them a few seconds later, as needed:
def nothing(self, *event):
""" # Hacking moment: A function that does nothing, for those times you need it...
"""
pass
def bind_tree(self):
""" # Bind mouse and keyboard events to their respective functions or methods...
"""
self.tree.bind('<<TreeviewSelect>>', self.selectItem_popup)
self.tree.bind('<Double-1>', self.show_details)
self.tree.bind("<Button-2>", self.popupMenu)
self.tree.bind("<Button-3>", self.popupMenu)
def unbind_tree(self):
""" # Unbind all mouse and keyboard events, by binding them to an empty method...
"""
self.tree.bind('<<TreeviewSelect>>', self.nothing)
self.tree.bind('<Double-1>', self.nothing)
self.tree.bind("<Button-2>", self.nothing)
self.tree.bind("<Button-3>", self.nothing)
Then, in the rest of the code, We only need to call bind_tree() and unbind_tree() as needed.
This worked for me:
tree.bind("<ButtonRelease-1>", my_select_function)
# Do some stuff
tree.unbind("<ButtonRelease-1>")

Resources