One button connected to two different event methods in PyQT - pyqt

I'm using PyQT4 to create a GUI. I have a button that needs to connect with one def when a certain condition is met. If this condition is not met, then the button should connect to another def. So far this is what I have -
if self.txtAgilent.text() and self.txtBattery.text():
self.connect(self.buttonPlot, SIGNAL('clicked()'), self.plotButtonClicked)
else:
self.connect(self.buttonPlot, SIGNAL('clicked()'), self.fileErrorMsgBox)
As above, if the first condition is met (i.e. if two files are selected), a plot is created. If not, an error message box pops up. The problem right now is that whenever I hit the plot button on my GUI, only the error message box pops up even after I have selected two files successfully.
The message box function is as follow:
def fileErrorMsgBox(self):
w = QWidget()
msg = QMessageBox.warning(w, "WARNING", "File(s) not chosen! Please choose a file.")
How do I solve this issue?

The best approach is not to try and change the method the button is connected to. Instead, keep the button connected to the same method, and within that method, performs the check (whether things are selected or appropriate text is in text boxes, whatever your use case).
So, for example, something like:
def __init__(self):
self.connect(self.buttonPlot, SIGNAL('clicked()'), self.plotButtonClicked)
def plotButtonClicked(self):
if self.txtAgilent.text() and self.txtBattery.text():
# do what plotButtonClicked did before
else:
# create and show the error message box
Note, you should really be using the new style signals/slot API in PyQt which performs connections like this:
self.buttonPlot.clicked.connect(self.plotButtonClicked)

Related

Can't update GtkIconView from within a click event on a GtkListBox in a GtkDialog

Here's what the click event currently looks like for the ListBox in the dialog code.
def OnSelectCategory(self, listbox, data=None):
try:
# Get the text of the selected category.
selected = listbox.get_selected_row()
label = selected.get_child()
itemText = label.get_text()
# Get the tags for the selected category.
tagtext = self.categoryTags.get(itemText)
self.updateStatusbar("Collecting videos...")
# Start a thread to scan for videos.
self.threadEvent = threading.Event()
self.videoscanThread = threading.Thread(target=self.ScanForVideos, args=(self.threadEvent, tagtext,))
self.videoscanThread.daemon = True
self.videoscanThread.start()
self.threadEvent.set()
except Exception as e:
print("Exception from 'OnSelectCategory':", str(e))
At first, I could not get the status bar to update the text immediately. I had originally called the function to update the text directly. The status bar text would not update until the ScanForVideos function had finished. So, I moved the ScanForVideos code into a thread. The thread waits on an event to begin.
The thread (ScanForVideos) runs several 'for' loops looking for a matching condition. When the condition is found, the code appends to the liststore for the IconView. At the end of the thread function, the code sets the IconView model to the liststore. The IconView seems to update with a few items, but, not all that should be there. Additionally, the code seems to be 'hung' because I cannot dismiss the dialog that contains the IconView. I have to stop debugging within Visual Studio Code.
I feel like I'm violating something I'm not aware of in Python coding. Or, my design to update the IconView is not correct. Can anyone shed some light on what I may be doing wrong?
I realized that I was trying to update some UI widgets from within my background thread. I then stumbled upon GLib.idle_add. I wrote a separate function to update the UI widgets and called idle_add passing the name of the function. This allowed me to update the GUI from a background thread.

Control close event pyglet

I have a program that has multiple windows in pyglet, and I want to make one window unclosable, rather to set its visibility to false. Is there a way I can access the event handle for close, to make it instead of closing the window, possibly return a bool or a string like "should close."?
Found the answer to my own question, first define a custom event loop with window_event_loop = pyglet.app.EventLoop(), then create a handler for .event for the event loop
#window_event_loop.event
def on_window_close(window):
# Extra code here
return pyglet.event.EVENT_HANDLED

tkinter button bind() bug

so i wanna bind an enter button to the function so it will work when i press the button with enter button.
but, everytime i do that the button always has a weird bug sometime it always getting in a pressed state or sometime the button act as i write an foregroundonactive parameter in the code.
i always write like this
def itung():
import speedtest
st = speedtest.Speedtest()
lbl['text']=round(st.download/1000000,2)
lbl= Label(root,width=5,height=3)
lbl.place(x=n,y=n)#n here is just an example
btn = Button(root, text='tombol', bg='brown', fg='yellow')
btn.place(x=n,y=n)#n here is just an example
btn.bind('<Return>',itung)
so can someone help me?
You should try this def itung(event = None): in your function. This ensures that the itung receives a None argument as expected, when the key is pressed.
Hi #Dsterror and welcome to the Stack Overflow community! I've seen your question and it seems you just need a little tweak in your code:
#add *args in your parameters for the itung function
def itung(*args):
import speedtest
st = speedtest.Speedtest()
lbl['text']=round(st.download/1000000,2)
lbl= Label(root,width=5,height=3)
lbl.place(x=n,y=n)#n here is just an example
btn = Button(root, text='tombol', bg='brown', fg='yellow')
btn.place(x=n,y=n)#n here is just an example
btn.bind('<Return>',itung)
You can replace the *args with any other parameter variable
Now some explanation.
Whenever you bind a key or a mouse button to a widget, tkinter expects you to create a parameter in the function which gets triggered. You may ask why is this though? What's the relevance? Imagine a left mouse button keybind to the Entry widget. Whenever you click on it, a function should get triggered. Sometimes, we may need the x and y coordinates of the cursor at the time of clicking on the Entry. So tkinter stores this in a list and gives it to the function you're binding to as a parameter. This is why we put in the *args.
Hope you understood something. If not, please tell me the unclear points

How can i make sure that every time i press a button in my kivy app a page gets run again

The code below is one of the pages of my app. The file usernames.txt gets updated with the name of the account whose button I have clicked. The problem is that get user_dates() function gets activated as soon as I run the code. So it gets the username I signed in with the last time and hence it gets the wrong dates. How can i make sure that as soon as I enter my account this page reloads again and user_dates() gets the updated username so that it gets into correct folder and picks up the right dates?
class DatesPage(FloatLayout):
def __init__(self, **kwargs):
super(DatesPage, self).__init__(**kwargs)
self.all_dates, self.username = self.get_user_dates()
print(self.username)
self.dates_dict = {}
self.butt_height = 0.7
for x in range(len(self.all_dates)):
self.dates_dict[x] = Button(text=self.all_dates[x], size_hint=(0.2, 0.1),
pos_hint={'x':0.15, 'y':self.butt_height})
self.add_widget(self.dates_dict[x])
self.butt_height -= 0.1
def get_user_dates(self):
with open('.\\req files\\usernames.txt', 'r') as file:
username = file.read()
for root, folders, files in os.walk(f'.\\req files\\{username}'):
files = files
files = [re.sub('.txt', '', file) for file in files]
return files, username
I am not 100% sure I understood your question, but I think what you want is get an updated page each time you enter the account page, thus call the user_dates() method only when you enter my account.
Well if that is what you trying to do, I think the problem with your code is that you are placing all that code inside the init method of DatesPage class, and because kivy initializes all the init methods of all it's widgets when it starts that causes your code here to be run automatically when kivy starts.
If that is not what you want, then you should place your code in a method other than the init method, and bind an event, or button event, to the callback of that method.
So when you press that button to enter my account the binded event callback will run that method and excecute the code here, and thus my account page will get updated when you open it.

How to tell if user has released slider

There's an issue on the github to add a released() signal to the slider node, but how would I do the same thing without it?
I want to have a slider, and when the user moves it it says "Value is now X" on a label on the screen. But when I do it based on the 'value_changed(x)' it calls many times while the slider is being dragged. I want it to set my only label when the player releases after sliding, or when presses and releases an area on the slider's range to select a new value without using the grabber.
Okay, this is what I've come up with. It doesn't literally let me know when the slider is released, but it tells me when the player stops editing the slider. It still sends an alert if you pause briefly, but that is okay for my game. It doesn't send continuous alerts like if you just use _on_HSlider_value_changed(), which is what I wanted to avoid.
var old = self.value #start value of slider
var timer_on = false
#will be called continuously while editing timer
func editing_slider(new):
#only start a timer, if there isn't one already or you'll have a million
if not timer_on:
#start timer
timer_on = true
yield(get_tree().create_timer(.2), "timeout" )
timer_on = false
#if still editing, re call function
if old != new:
editing_slider(new)
#done editing
else:
print("slider set to " + str(value))
old = new
func _on_HSlider_value_changed(value):
editing_slider(value)
If you wanted to avoid the alert being called when the user pauses but hasn't released, you'd have to do do some kind of InputEvent check.
You can achieve what you want by overriding the _gui_input function. Attach a script to your slider, and then add this code:
func _gui_input(event):
if (event is InputEventMouseButton) && !event.pressed && (event.button_index == BUTTON_LEFT):
print("Released")
This will work whether the user releases the grabber or "releases an area on the slider's range to select a new value without using the grabber", and achieves what you want. However, if the code is meant to run on a device with a keyboard (e.g. a PC), then the user can also change the value via the cursor keys on the keyboard, and you may want to add support for that too.

Resources