Dears,
How To Secure Collapsible / Expandable Frames with password.
I have used the Script shared in : https://github.com/PySimpleGUI/PySimpleGUI/issues/295
Here's a reduced part of it, just to clarify my query :
import PySimpleGUI as sg
layout1 = [[sg.Text('This is layout 1 - It is all Checkboxes')],
*[[sg.CB(f'Checkbox {i}')] for i in range(3)]]
layout2 = [[sg.Text('This is layout 2')],
[sg.Input(key='-IN-')],
[sg.Input(key='-IN2-')]]
layout = [[sg.Column(layout1, key='-COL1-'), sg.Column(layout2, visible=False, key='-COL2-')],
[sg.Button('1'), sg.Button('2'), sg.Button('Exit')]]
window = sg.Window('Swapping the contents of a window', layout)
while True:
event, values = window.read()
print(event, values)
if event in (sg.WIN_CLOSED, 'Exit'):
break
elif event in '123':
window[f'-COL{layout}-'].update(visible=False)
layout = int(event)
window[f'-COL{layout}-'].update(visible=True)
window.close()
What should be the event to be added , so that , once we click on Button "2" , it will ask for User/Password to be able to access inside that frame. Thanks in advance
Related
Im trying to update the background color of my checkbox when it is clicked. However, the moment i call the update method, somehow another event on the same element is triggered, resulting in unexpected behavior. Can somebody tell me how to achieve that?!
My minimal Code is as follows:
import PySimpleGUIQt as sg
layout = [
[sg.Checkbox('test', enable_events=True, key='test', background_color="green",default=True)]
]
window = sg.Window('Sample GUI', layout, finalize=True)
while True: # Event Loop
event, values = window.read(timeout=100)
if event == sg.WINDOW_CLOSED:
break
elif event == "test":
if not values[event]:
window[event].update(background_color="red")
else:
window[event].update(background_color="green")
window.close()
Qt port is still under revision, not everything work fine.
Try also to set the value at the same time for the checkbox when you set the background_color.
import PySimpleGUIQt as sg
layout = [[sg.Checkbox('test', enable_events=True, key='test', background_color="green",default=True)]]
window = sg.Window('Sample GUI', layout, finalize=True)
while True: # Event Loop
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == "test":
value = values[event]
if not value:
window[event].update(value=value, background_color="red")
else:
window[event].update(value=value, background_color="green")
window.close()
In tkinter , Buttons have a parameter called command which causes some function to run whenever the button is clicked.
Is the same possible to do with the options of a tkinter Combobox ?
For example :
I have three options in the tkinter Combobox : #1 , #2 , #3
Now , As soon as #1 is or clicked , I want to print something like Options 1 Selected.
Is it possible to do ?
P.S , As #Atlas435 pointed out about getting the selected value from the tk.Combobox() , I really dont want the value of the Combobox() , what I am asking is that whether there is way to make Combobox options work like Buttons which when clicked , causes some function to execute.
Take a look at this example, which does what you are asking for:
from tkinter import *
from tkinter import ttk
root = Tk()
def select(event): #the function to get triggered each time you choose something
if c.get() == lst[0]: #if it is the first item
print('Option 1 is selected')
elif c.get() == lst[1]: #if it is the second item
print('Option 2 is selected')
else: #or if it is the third item
print('Option 3 is selected')
lst = ['#1','#2','#3'] #creating option list
c = ttk.Combobox(root,values=lst,state='readonly') #creating a combobox
c.current(0) #setting first element as the item to be showed
c.pack(padx=10,pady=10) #putting on screen
c.bind('<<ComboboxSelected>>',select) #the event that your looking for
root.mainloop()
Ive commented it to understand better, if any doubts or errors, do let me know
In my application I have multiple windows that change based on events(one close and another open) and show only one window at a time. During one window close and another open its take some time since fetch data from database and prepare for window.
Here problem is that during the time of one window close and another open user can see and feel that one is being open and another is being close by seeing the background.
What I want, until second screen is not fully loaded, first window be visible on the screen.
My current code is something like,
import PySimpleGUI as sg
layout = [[sg.Button('Users', key='show_user_list')]]
window = sg.Window('users').Layout(layout)
while True:
event, values = window.Read()
if event == 'show_user_list':
window.Close()
# code ommited here for simplicity
# do mysql stuff to fetch data
# layout2 = ...
# window2 = sg.Window('user listing').Layout(layout2)
# while True:
# event, values = window2.Read()
# ...
# like that I have multiple windows
else:
pass
How I can give users feel like the window content is changing not one window closes and another opens?
Sure, you can make sure the background is not seen by first opening your Window 2, which will be created on top of Window 1, THEN closing your Window 1.
To do this, add a .Finalize() onto the Window 2 creation. This will cause the window to immediate show up. Then on the next line, close Window 1.
import PySimpleGUI as sg
layout = [[sg.Button('Users', key='show_user_list')]]
window = sg.Window('users').Layout(layout)
while True:
event, values = window.Read()
if event == 'show_user_list':
# code ommited here for simplicity
# do mysql stuff to fetch data
# layout2 = ...
# window2 = sg.Window('user listing').Layout(layout2).Finalize()
# window.Close()
# while True:
# event, values = window2.Read()
# ...
# like that I have multiple windows
else:
pass
The key to making this kind of window update work is to create the windows at the same location. The default is to make windows that are centered on the screen. This means if your windows are not the same size then you'll likely notice a small "blip" as you change from one to the other. But it shouldn't look bad because it'll happen so quickly.
If you really want to get fancy, you can add another step which will make the switch between the windows even smoother. This new step involves creating window 2 with Alpha=0, meaning that it's invisible, then after it's fully formed (using Finalize()) you change the Alpha to 1 which will make the window appear.
import PySimpleGUI as sg
layout = [[sg.Text('Example of window-replacement')],
[sg.Combo(['abdeffg', 'rrrfwwew'], size=(10, 4))],
[sg.B('Enable Filter'), sg.B('Warning'), sg.B('Reopen')],]
window = sg.Window('My Text Editor', layout)
while True: # Event Loop
event, values = window.Read()
if event is None:
break
print(event, values)
if event == 'Reopen':
layout2 = [[sg.Text('This is a completely different window')],
[sg.Combo(['abdeffg', 'rrrfwwew'], size=(10, 4))],
[sg.B('Enable Filter'), sg.B('Warning'), sg.B('Reopen')], ]
window2 = sg.Window('My Text Editor', layout2, alpha_channel=0).Finalize()
window2.SetAlpha(1)
window.Close()
window = window2
window.Close()
This removed some of the "painting" of the window that I was seeing. That shouldn't happen because I use this same trick when creating the window to begin with. Alpha is used to hide the window while it's being created.
import PySimpleGUI as sg
layout = [[sg.Button("OK1", key='1', bind_return_key=True)],
[sg.Button("OK2", key='2', bind_return_key=True)],
[sg.Button("OK3", key='3', bind_return_key=True)]]
window = sg.Window("Keyboard Test", return_keyboard_events=True).Layout(layout)
while True:
event, values = window.Read()
print(event)
if event == None:
break
When I run the above code and perform mouse click operation, I get the output as following that is as expected,
clicking on OK1 Button print in the console as: 1
clicking on OK2 Button print in the console as: 2
clicking on OK3 Button print in the console as: 3
But, when I perform keyboard event means, I visit on the button through tab key of keyboard and press Enter on that Button via keyboard it return the same key on all these three button,
Visit on these button one by one via tab key and then press Enter on each I get the result as,
Press Enter on OK1 Button print in the console as: 1
Press Enter on OK2 Button print in the console as: 1
Press Enter on OK3 Button print in the console as: 1
That is not expected output. What I want it should also print their own key same as mouse click event.
What is my intention:
When user press Enter button over OK1, It should print('Hello, OK1 pressed.')
When user press Enter button over OK2, It should print('Hello, OK2 pressed.')
When user press Enter button over OK3, It should print('Hello, OK3 pressed.')
When user click on OK1, It should print('Hello, OK1 pressed.')
When user click on OK2, It should print('Hello, OK2 pressed.')
When user click on OK3, It should print('Hello, OK3 pressed.')
How to achieve this?
I finally understood the question and wrote this as a solution. It's been turned into a Demo Program for the project because people do expect the ENTER key to click a button and yet tkinter and Qt both seem to not work this way. They select buttons using the spacebar.
This source code works for both PySimpleGUI and PySimpleGUIQt. Just use the correct import statement at the top.
import PySimpleGUI as sg
# import PySimpleGUIQt as sg
QT_ENTER_KEY1 = 'special 16777220'
QT_ENTER_KEY2 = 'special 16777221'
layout = [ [sg.T('Test of Enter Key use')],
[sg.In(key='_IN_')],
[sg.Button('Button 1', key='_1_')],
[sg.Button('Button 2', key='_2_')],
[sg.Button('Button 3', key='_3_')], ]
window = sg.Window('My new window', layout,
return_keyboard_events=True)
while True: # Event Loop
event, values = window.Read()
if event is None:
break
if event in ('\r', QT_ENTER_KEY1, QT_ENTER_KEY2): # Check for ENTER key
elem = window.FindElementWithFocus() # go find element with Focus
if elem is not None and elem.Type == sg.ELEM_TYPE_BUTTON: # if it's a button element, click it
elem.Click()
# check for buttons that have been clicked
elif event == '_1_':
print('Button 1 clicked')
elif event == '_2_':
print('Button 2 clicked')
elif event == '_3_':
print('Button 3 clicked')
Out of all of the code above, the part that implements this feature is contained in these 4 lines of code. Add these lines of code to your event loop and then your window will behave such that the ENTER key will click a button. The rest of the code does things with the clicks, is the layout, etc. It's really these statements that implement it.
if event in ('\r', QT_ENTER_KEY1, QT_ENTER_KEY2): # Check for ENTER key
elem = window.FindElementWithFocus() # go find element with Focus
if elem is not None and elem.Type == sg.ELEM_TYPE_BUTTON: # if it's a button element, click it
elem.Click()
I don't fully understand what your goal is. What behavior do you want?
When a button has the focus, pressing the return key doesn't seem to trigger buttons by default. The space bar will though.
Here is some code that will click a button based on a user's keyboard input. I THINK that was what you described after I re-read your question a few times.
import PySimpleGUI as sg
layout = [[sg.Button("OK1", key='_1_', )],
[sg.Button("OK2", key='_2_', )],
[sg.Button("OK3", key='_3_', )]]
window = sg.Window("Keyboard Test", layout, return_keyboard_events=True)
while True:
event, values = window.Read()
# window.Maximize()
print('event: ', event)
if event == None:
break
if event == '1':
window.Element('_1_').Click()
elif event == '2':
window.Element('_2_').Click()
elif event == '3':
window.Element('_3_').Click()
window.Close()
If you want to control the buttons using the keyboard, then what you'll have to do is collect the keyboard key presses and then convert those into whatever behavior you want.
Pressing ENTER when a button has focus (is highlighted) does NOT generate a button click, a SPACE BAR does. You can see this demonstrated in this tkinter program:
import tkinter as tk
def write_slogan():
print("Tkinter is easy to use!")
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
button = tk.Button(frame,
text="QUIT",
fg="red",
command=quit)
button.pack(side=tk.LEFT)
slogan = tk.Button(frame,
text="Hello",
command=write_slogan)
slogan.pack(side=tk.LEFT)
root.mainloop()
[EDIT - sorry about the edits but I'm experimenting... ]
I have a QScrollArea with a QWidget (content_widget) inside in my application. I want to fill it with images while the application is running. I would like to have the scroll behavior if the number of images exceeds the number which can be shown without scrolling. I came this far:
children_occupied_width = self.ui.content_widget.childrenRect().width()
image_view = QtGui.QGraphicsView(self.ui.content_widget)
image_view.setGeometry(QtCore.QRect( children_occupied_width, 0, 210, 210 ))
image_view.setObjectName("New Image")
placeholder_image = QtGui.QGraphicsScene()
placeholder_image.addPixmap(QtGui.QPixmap('placeholder.png'))
image_view.setScene(placeholder_image)
image_view.show()
Although the images appear in the list at the right position there is no scrolling if the images start to be placed outside of the visible area. The size of content_widget seems not to change even with
self.ui.content_widget.resize(...)
or
self.ui.content_widget.adjustSize()
How to make it grow/resize?
This first part turned out to not be what you really needed. I am leaving it for informational purposes, but see the update at the bottom
The problem is that the QGraphicsView is itself a type of scroll widget, representing a slice of the scene it is viewing. Ideally you would just use the view by itself and make use of its own scrollbars.
But if you have specific need to forward the content size of the view up to a normal QWidget scroll, then what you would need to do is make your QGraphicsView always resize itself when the contents of the scene change. The QScrollArea is only going to respond to size changes of the widget it is set to. The view needs to change size. The process would be that the view needs to listen to signals from the scene for added or removed items, and then resize itself to completely enclose all of those children.
Here is an example of how the QGraphicsView widget, on its own, is perfectly capable of serving as the scroll functionality:
from PyQt4 import QtCore, QtGui
app = QtGui.QApplication([])
scene = QtGui.QGraphicsScene()
view = QtGui.QGraphicsView()
view.setScene(scene)
view.resize(600,300)
pix = QtGui.QPixmap("image.png")
w,h = pix.width(), pix.height()
x = y = 0
pad = 5
col = 3
for i in xrange(1,20):
item = scene.addPixmap(pix)
item.setPos(x,y)
if i % col == 0:
x = 0
y += h+pad
else:
x+=w+pad
view.show()
view.raise_()
app.exec_()
You can see that when the images overflow the current size of the view, you get scrollbars.
If you really need to have some parent scroll area acting as the scroll for the view (for reasons I do not really understand), then here is a more complex example showing how you would need to watch for some event on the scene and then constantly update the size of the view to force scrollbars on the parent scrollarea. I have chosen to watch a signal on the scene for when its rect changes (more children are added)
from PyQt4 import QtCore, QtGui
app = QtGui.QApplication([])
win = QtGui.QDialog()
win.resize(600,300)
layout = QtGui.QVBoxLayout(win)
scroll = QtGui.QScrollArea()
scroll.setWidgetResizable(True)
layout.addWidget(scroll)
view = QtGui.QGraphicsView(scroll)
view.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
view.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
scene = QtGui.QGraphicsScene(scroll)
view.setScene(scene)
scroll.setWidget(view)
pix = QtGui.QPixmap("image.png")
w,h = pix.width(), pix.height()
x = y = i = 0
pad = 5
col = 3
def createImage():
global x,y,i
item = scene.addPixmap(pix)
item.setPos(x,y)
i+=1
if i % col == 0:
x = 0
y += h+pad
else:
x+=w+pad
def changed():
size = scene.itemsBoundingRect().size()
view.setMinimumSize(size.width()+pad, size.height()+pad)
scene.changed.connect(changed)
t = QtCore.QTimer(win)
t.timeout.connect(createImage)
t.start(500)
win.show()
win.raise_()
app.exec_()
The scroll area is always looking at the size of the widget that has set as the child. The view must be resized.
Update
As it turns out from your comments, you didn't really need to use the QGraphics objects for this approach. It only made your task more complicated. Simply use a vertical layout and add QLabel widgets to it:
from PyQt4 import QtCore, QtGui
app = QtGui.QApplication([])
win = QtGui.QDialog()
win.resize(300,300)
layout = QtGui.QVBoxLayout(win)
scroll = QtGui.QScrollArea()
scroll.setWidgetResizable(True)
layout.addWidget(scroll)
scrollContents = QtGui.QWidget()
layout = QtGui.QVBoxLayout(scrollContents)
scroll.setWidget(scrollContents)
pix = QtGui.QPixmap("image.png")
def createImage():
label = QtGui.QLabel()
label.setPixmap(pix)
layout.addWidget(label)
t = QtCore.QTimer(win)
t.timeout.connect(createImage)
t.start(500)
win.show()
win.raise_()
app.exec_()
I found at least a reason why resize did not worked in my code. Setting widgetResizable property of QScroll area to false (or disabling it in the Qt Designer) made it work.