how to know if a GTK window is minimized in ruby - ruby

i want to know if the window is minimized or not. i have connected window-state-event signal from GtkWidget to this function
def on_main_window_hide(object, event)
if event.changed_mask & Gdk::WindowState::ICONIFIED
if event.new_window_state & Gdk::WindowState::ICONIFIED
puts("minimize" + $counter.to_s)
$counter+=1
else
puts ("unminimize")
end
end
end
and even after doing minimizing and unminimizing couple of times .. it never prints if the window is unminimized, here is the output
minimize0
minimize1
minimize2
minimize3
minimize4
plus, minimizing gives the window-state-event signal twice, like if minimize0 is initial value then on minimizing it becomes minimize2
how can i detect properly if a window is minimized ?

I think you got confused by the callback name. The window-state-event signal gets emitted for every state event change, so the callback should be called on_main_window_state_event as it will be emitted not only when the window hides. Then you can check the event mask for changes.
Your callback has a redundant if condition. From the API, the changed_mask is a:
mask specifying what flags have changed
And window_new_state is also a mask but contains:
the new window state, a combination of GdkWindowState bits
This means that the second if condition will always evaluate as true if the first one also evaluates as true but the else branch will never happen. If you want your "unminimize" print to occur then you must remove the second if condition and move the print to the else branch of the first if condition. Another option would be to check for the bit mask and really check the status.
Your code should be:
def on_main_window_state_event(object, event)
if event.changed_mask & Gdk::WindowState::ICONIFIED
puts("minimize" + $counter.to_s)
$counter+=1
else
puts ("unminimize")
end
end

Related

How do I prevent multiple discordrb bot activations being processed out of sequence?

I have a Ruby Discord (discordrb) bot written to manage D&D characters. I notice when multiple players submit the same command, at the same time, the results they each receive are not independent. The request of one player (assigning a weapon to their character) ends up being assigned to other characters who submitted the same request at the same time. I expected each request to be executed separately, in sequence. How do I prevent crossing requests?
bot.message(contains:"$Wset") do |event|
inputStr = event.content; # this should contain "$Wset#" where # is a single digit
check_user_or_nick(event); pIndex = nil; #fetch the value of #user & set pIndex
(0..(#player.length-1)).each do |y| #find the #player pIndex within the array using 5 char of #user
if (#player[y][0].index(#user.slice(0,5)) == 0) then pIndex = y; end; #finds player Index Value (integer or nil)
end;
weaponInt = Integer(inputStr.slice(5,1)) rescue false; #will detect integer or non integer input
if (pIndex != nil) && (weaponInt != false) then;
if weaponInt < 6 then;
#player[pIndex][1]=weaponInt;
say = #player[pIndex][0].to_s + " weapon damage has be set to " + #weapon[(#player[pIndex][1])].to_s;
else;
say = "Sorry, $Wset requires this format: $Wset? where ? is a single number ( 0 to 5 )";
end;
else
say = "Sorry, $Wset requires this format: $Wset? where ? is a single number ( 0 to 5 )";
end;
event.respond say;
end;
To avoid race conditions in multithreaded code like this, the main thing you want to look for are side effects.
Think about the bot.message(contains:"$Wset") do |event| block as a mini program or a thread. Everything in here should be self contained - there should be no way for it to effect any other threads.
Looking through your code initially, what I'm searching for are any shared variables. These produce a race condition if they are read/written by multiple threads at the same time.
In this case, there are 2 obvious offenders - #player and #user. These should be refactored to local variables rather than instance variables. Define them within the block so they don't affect any other scope, for example:
# Note, for this to work, you will have to change
# the method definition to return [player, user]
player, user = check_user_or_nick(event)
Sometimes, making side effects from threads is unavoidable (say you wanted to make a counter for how many times the thread was run). To prevent race conditions in these scenarios, a Mutex is generally the solution but also sometimes a distributed lock if the code is being run on multiple machines. However, from the code you've shown, it doesn't look like you need either of these things here.

Play sound if space is not pressed in Psychopy

I am trying to get a sound to play if 'Space' is not pressed AND if time is >1s, and to stop it if 'Space' is pressed
alarm = sound.Sound(700, secs = 5)
if (len(event.getKeys())==0) & (time1.getTime() > 1):
alarm.play()
elif event.getKeys(keyList = 'space'):
alarm.stop()
However, when I do this, I cannot press 'space' to stop the alarm.
Can anyone tell me what I am doing wrong here? Is there any thing wrong with the '(len(event.getKeys())==0)' portion?
In Matlab, I can just write
if ~KbCheck && .....
but I am not sure how to do it in Psychopy.
Thanks!
Use event.getKeys(keyList=['space']). The keyList is a list, not a string. Also, change the order of your logic since the first call to event.getKeys() clears the keyboard buffer for all keys, since no keyList was provided so that nothing is left to be registered in your elif.
So this should do the trick:
if event.getKeys(keyList = ['space']):
alarm.stop()
elif time1.getTime() > 1 and not event.getKeys(): # if another key was pressed... #added brackets here
alarm.play()
As you can see, I also simplified the syntax for one of the tests.

QProgressBar causing bad performance in QT5?

I'm developping a program which parses a file (365000 lines) in which I try to match some keywords after reading each line. This computation along with the update of my QProgressBar are made in another thread using QThread. Everything works fine except for the performance especially when I update the QProgressBar. I use a timer for the parsing and the result is just STUNNING. When I emit a signal to update the QProgressBar the program takes around 45 seconds but when I do not emit the signal for the QProgressBar update then the program takes around 0.40 sec =/
from PyQt5 import QtCore, QtWidgets, QtGui
import sys
import time
liste = ["failed", "exception"]
class ParseFileAsync(QtCore.QThread):
match = QtCore.pyqtSignal(str)
PBupdate = QtCore.pyqtSignal(int)
PBMax = QtCore.pyqtSignal(int)
def run(self):
cpt = 0
with open("test.txt", "r") as fichier:
fileLines = fichier.readlines()
lineNumber = len(fileLines)
self.PBMax.emit(lineNumber)
t0 = time.time()
for line in fileLines:
cpt+=1
self.PBupdate.emit(cpt)
for element in liste:
if element in line:
self.match.emit(line)
finalTime = time.time() - t0
print("over :", finalTime)
class Ui_MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.thread = ParseFileAsync()
self.thread.match.connect(self.printError)
self.thread.PBupdate.connect(self.updateProgressBar)
self.thread.PBMax.connect(self.setMaximumProgressBar)
self.pushButton_GO.clicked.connect(self.startThread)
def printError(self, line):
self.textEdit.append(line)
def updateProgressBar(self, value):
self.progressBar.setValue(value)
def setMaximumProgressBar(self, value):
self.progressBar.setMaximum(value)
def startThread(self):
self.thread.start()
Console output:
over : 44.49321101765038 //QProgressBar updated
over : 0.3695987798147516 //QProgressBar not updated
Am I missing something or is that expected ?
EDIT :
I followed jpo38 and Matteo very good advices. I update the QProgressBar less frequently. The progression is still smooth and the performance is very good (around one second with this implementation). PSB :
class ParseFileAsync(QtCore.QThread):
match = QtCore.pyqtSignal(str)
PBupdate = QtCore.pyqtSignal(int)
PBMax = QtCore.pyqtSignal(int)
def run(self):
with open("test_long.log", "r") as fichier:
fileLines = fichier.readlines()
self.lineNumber = len(fileLines)
self.PBMax.emit(self.lineNumber)
if (self.lineNumber < 30):
self.parseFile(fileLines, False)
else:
self.parseFile(fileLines, True)
def parseFile(self, fileLines, isBig):
cpt = 0
if(isBig):
for line in fileLines:
cpt+=1
if(cpt % (int(self.lineNumber/30)) == 0):
self.PBupdate.emit(cpt)
for element in liste:
if element in line:
self.match.emit(line)
self.PBupdate.emit(self.lineNumber) #To avoid QProgressBar stopping at 99%
else:
for line in fileLines:
cpt+=1
self.PBupdate.emit(cpt)
for element in liste:
if element in line:
self.match.emit(line)
Updating a QProgressBar too often will definitely lead to performance issues. You should update the progress bar less often. You don't want/need to do that for every iteration...365000 times. When you read one line out of 365000, you progressed by 0.0002%, no need to update GUI for that...
Showing progression to user always has a cost...and we accept that because user prefers to wait a bit more and have progress information. However, showing progression must not multiply processing time by 100 as you experienced.
You can either emit the signal to update the progress bar only when progression significantly changed (for instance every time percentage value casted to a int changed, you can store a progression as int value to check that...or test if (line%(fileLines/100)==0) for instance...this will significantly decrease the cost of progress bar update).
Or you could start a QTimer to update the progress bar every 100ms for instance. Then you don't emit any signal from the for loop and just save the progression value to be used when timer times out.
If the file size is always 365000 lines, you can also decide to emit the signal every 1000 lines for instance (if line%1000==0). But the two earlier solutions are preferable because they will fix your performance issues whatever the file size is.
That's a classic problem, every experienced developer I know have a story about a supposedly long process where most of the time was actually taken by the progressbar update (most of these stories end up with removing the progress bar completely).
The point is that, very often, the "unit of work" you process (in your case the parsing of a line) is way smaller than the cost of a progress bar update - GUIs are fast compared to the user reflexes, but are still quite heavyweight when compared to, say, parsing a single line (especially if cross-thread machinery is involved).
In my experience, there are three common solutions:
if you notice that your process in general is "fast" you just drop the progressbar (or replace it with those unhelpful "forward and backwards" progress bars just to show that your program isn't hung if the program is some times fed with files way bigger than usual);
you just update it less frequently; you can emit your signal every 1/100 of your total progress; the advancement will be still smooth and you should not have performance problems (100 updates aren't going to take much time, although I guess they'll still dominate the time taken by your process if it normally takes 0.40 seconds) ;
you can decouple completely the progressbar update from the code that actually does the stuff. Instead of emitting a signal, update an integer class member with the current progress (which should be quite cheap); in the GUI thread use a timer to update the progressbar according to this member every - say - 0.5 seconds. You can even be smarter and avoid showing the progressbar completely if the process finishes before the first timer tick.

Ruby: How to make GLib::Timeout.add_seconds($second) use an array rather than one static value?

When I use $second, which is an array of integers, GLib::Timeout.add_seconds($second) loads the first number in the stack and then stays the same, it keeps using that one value each iteration. I would like the timeout to load the next number in the array, and continue to the next, instead of using only the first number. The timeout seems static once it is set, is there a way to approach dynamic/changing timeout values, or loading an array as a timeout in the bellow context?
def time
true
end
activator = Activator.new
activator.window.signal_connect("key_press_event") do |_window, event|
case event.keyval
when Gdk::Keyval::GDK_KEY_space
activator.time = !activator.time
end
end
activator.window.set_default_size(556, 900)
activator.window.add(activator.box)
activator.window.show_all
GLib::Timeout.add_seconds($second) do
activator.nuInstance if activator.time
true
end
Thank you for your help!
It sure doesn't look like it. GLib::Timeout.add_seconds() just calls down to g_timeout_add_seconds_full. It calls its function and if it returns true it'll call it again later, false and it'll stop.
Something to try would be instead of returning true, queue up the next timeout with the next time value from the list and return false, ending that particular timeout.

ROR code sends many mails

I am having following ROR code
def func (point,c,r)
res=isPointInCircle(point,c,r) #returns true/ false
if res=='false'
AlertMailer.geofence('mail#gmail.com').deliver
end
end
func is called for every 2 minutes with different point.
isPointInCircle checks point is within circle or not .Returns true false.
I want to send mail when the point is first time outside or inside the circle.
Means if initially point is inside for 1 hrs.
no mail
if it goes outside
I want single mail to be sent which does not happen,
because func is called every 2 min.
so emails are sent every 2 minutes which i dont want .
So how to do this
You could check if the previous status is the same as the current status, and only send the mail if there has been a change (the point is inside the circle and it was outside before, or vice versa).
def func(point, c, r)
status = point_in_circle?(point, c, r)
if last_status != status
AlertMailer.geofence('mail#gmail.com').deliver
end
end
It's hard to say how to implement last_status without knowing more about your app. If there were a Point model, I might add in_circle? as an instance method. Then you could get the previous point from the database and compare the two:
def check_point_status(current, previous, c, r)
if current.in_circle?(c, r) != previous.in_circle?(c, r)
AlertMailer.geofence('mail#gmail.com').deliver
end
end
A couple of pointers: Ruby usually uses snake_case not camelCase for variables and functions, and "predicate" methods (returning true/false) are typically named with a question mark at the end to indicate that they are yes/no questions. You wouldn't have needed the comment in your code because it would have been obvious that point_in_circle? returns true or false.
Also your code suggests your function returns "true" or "false" as strings, which is a really bad idea! If you used the actual boolean values true or false, you could have written your original code as
def func(point, c, r)
unless point_in_circle?(point, c, r)
AlertMailer.geofence('mail#gmail.com').deliver
end
end

Resources