How to open a Glade-created window and wait for it to close, in Ruby? - ruby

I want to make my own window, using Glade (3.14.2).
At a certain point in my program, I want to
1) Put up the window and let the user do stuff
2) Wait for it to close
3) Get values from the window object
4) Continue on in my code
So basically, I want to treat the window like a modal dialog - but one that I write and control.
I've tried for a few hours. The window appears just fine, as designed in Glade. The user can interact with it.
When the window closes, code that's been connected with signal_connect('destroy') executes.
But the code that invoked the window's show() method... does not continue executing after the window closes.
class GrammarNodeEditor
#this makes the class visual:
include GladeGUI
def initialize(raw_node = nil, &close_block)
#raw_node = raw_node || {type: :Sequence, data: []}
#original_data = #raw_node[:data]
#close_block = close_block
end
def show
puts "GNE Window Opening"
load_glade(__FILE__)
#builder["window1"].title = "Edit/Create Grammar Node"
#builder["window1"].signal_connect('destroy') {|*args|
#close_block.call(self)
puts "GNE WINDOW DESTROY"
}
show_window()
puts "Done showing window"
end
Here is how I invoke it:
rhs_editor = GrammarNodeEditor.new {|obj|
puts "In closeblck, obj is #{obj.inspect}"
#rhs = obj.raw_node
}
puts "About to call show in GR:Init"
rhs_editor.show
puts "Back from calling show in GR:Init"
Here is the output:
About to call show in GR:Init
GNE Window Opening
In closeblck, obj is #<GrammarNodeEditor:0x7b82a88 #raw_node={:type=>:Sequence, :data=>[]}, [more junk here]>
GNE WINDOW DESTROY
The first two lines of output appear after I open the window. The 3rd and 4th appear when I close the window.
Note that "Done showing window" and "Back from calling show in GR:Init" are not printed at all.
Just to make this a little more interesting, I want to be able to do this from within code that puts up another window. My top-level window has a button to create a new Rule. The Rule must be initialized with a Node, and then the Rule must be edited. So first I need to put up a Node-definition window (as shown above) and then, when I have a Node defined, I want to put up a Rule window that uses that Node.
So I think I need to call this code within either the initialize() or the show() method of the GrammarRuleWindow class (another Glade-defined window).
Can someone explain why my puts's aren't being printed, and how to make the control flow go on through them?
Thanks!

...So it turned out the problem was that I had created the window's .glade file directly in Glade, rather than using the VisualRuby IDE.
Creating the .glade in VR adds some stuff to the file that VR needs. Specifically, the file needs to contain the line
<signal name="destroy" handler="destroy_window" swapped="no"/>
before the first <child...> tag.

Related

Watir webdriver: new window hiding behind main window

I've been doing some dabbling with test automation using Ruby and Watir webdriver...I am running into an oddity where I open a new window, but when I act upon it, it performs all my actions behind the parent windowHere's how I set it up (using Firefox if that makes a difference):
# opens new window
b.button(:text => 'Upload').click
b.window(:url => 'urlname'). use do
# action performed on the new window
b.select_list(:id => 'selector').select 'Foo'
end
Note: it correctly performs actions on the select list present, but it does this behind the main browser.Is there something specific I need to write in order for it to remain in front of the main window?
This should work:
b.window(:url => 'urlname').focus
Put that line just after your .use line, and you should see the new window called to the foreground. You may actually find that you don't end up needing the .use line (or the do/end) at all.

PyQt QComboBox.setEditText leaves the entered text selected/highlighted; how to unhighlight?

UPDATE3 - SOLVED with reservations, please see my solution below; leaving it open since the cause of the problem is unclear, and I don't know how robust the solution is.
UPDATE1: here's the short short version.
Currently, after .setEditText on a QComboBox, I get this:
so the next thing you type will overwrite 'Team '.
But the desired effect is this (unhighlighted / unselected), so that the next thing you type will be appended to 'Team ' instead of overwriting it:
Thanks for any help. The rambling details are below, which is the original post:
(this is all PyQt 5.4)
UPDATE2:
Apparently python doesn't think anything is actually selected:
self.entryForm.ui.teamField.lineEdit().setText("Team ")
print("selected text:"+self.entryForm.ui.teamField.lineEdit().selectedText())
prints "selected text:" and nothing else. To make sure that's working:
self.entryForm.ui.teamField.lineEdit().setText("Team ")
self.entryForm.ui.teamField.lineEdit().setSelection(0,4)
print("selected text:"+self.entryForm.ui.teamField.lineEdit().selectedText())
prints "selected text:Team"
So that might be why many of the methods that affect selection are not working as expected (.deselect(), .setSelection(5,5), etc, and even some of the other methods give unexpected behavior, i.e. cursorForward(False,1) or cursorBackward(False,1) and such.
Original post:
This is for a radio log GUI, so keyboard interactions must be minimal and intuitive. openNewEntryForm (below) is called as a slot from a pushbutton on the main application GUI window:
self.ui.pushButton.clicked.connect(self.openNewEntryDialog)
It can also be called using a keyPressEvent in the same class:
def keyPressEvent(self,event):
if type(event)==QKeyEvent:
print("QKeyEvent:"+str(event.key()))
if event.key()==Qt.Key_T:
self.openNewEntryDialog('t')
event.accept()
else:
event.ignore()
Here's the method in question:
def openNewEntryDialog(self,key=None):
self.entryDialog=newEntryDialog()
if key=='t':
self.entryDialog.ui.to_fromField.setCurrentIndex(1)
self.entryDialog.ui.teamField.setFocus()
self.entryDialog.ui.teamField.setEditText("Team ")
if self.entryDialog.exec_():
self.newEntry(self.entryDialog.getValues()) # adds the log entry
so, the intended key press sequence is (from the main application GUI window):
a single keyboard press of 't' will open the entryForm, set the to_fromField to index 1 (which happens to be "TO"), give focus to teamField (also a QComboBox), set its text to "Team " and set itself up so that the very next keypress will appear as the text following "Team " in teamField.
So, starting from the main app GUI again, the plan is that typing 't3' should open the new entry window, set the to_fromField to "TO", and set the teamField to "Team 3", ready for a keypress of the tab key to move on to the next field in the entryForm.
The problem is that the teamField.setEditText("Team ") call leaves all of the text highlighted/selected, so that a subsequent key press of '3' would replace "Team " with "3"; I'm looking for a way to unhighlight/unselect "Team " but leave the cursor active at the right of that string, so that the subsequent key press of '3' would make the entire string "Team 3".
Ideas? Thanks in advance.
You can access the line-edit of the combo box, and then remove the selection:
self.entryDialog.ui.teamField.setEditText("Team ")
self.entryDialog.ui.teamField.lineEdit().deselect()
UPDATE:
The above code is correct, but it seems that the dialog will then clobber it when it initialises the focus handling for its child widgets after it is shown. If a dialog is opened with exec(), it will start its own event-loop, and some events (including focus events) will only be processed after it is fully shown. This is why it may appear that some changes made to child widgets before the dialog is shown are being ignored.
One way to work around this is to use a single-shot timer to ensure the changes are only attempted after the dialog is shown.
So add a method to the entry dialog class something like this:
def resetUI(self, key):
if key == 't':
self.ui.to_fromField.setCurrentIndex(1)
self.ui.teamField.setFocus()
self.ui.teamField.setEditText('Team ')
QtCore.QTimer.singleShot(0, self.ui.teamField.lineEdit().deselect)
and then use it like this:
def openNewEntryDialog(self, key=None):
self.entryDialog = newEntryDialog()
self.entryDialog.resetUI(key)
if self.entryDialog.exec_():
self.newEntry(self.entryDialog.getValues())
SOLVED with reservations, see UPDATE3 in the original post.
So, with the initial text all highlighted, tests show that it didn't actually think anything was selected. This solution was just stumbled upon by trial and error, fiddling with setting and clearing focus, selecting text and trying deselect:
def openNewEntryDialog(self,key=None):
self.entryForm=newEntryDialog()
if key=='t':
self.entryForm.ui.to_fromField.setCurrentIndex(1)
self.entryForm.ui.teamField.lineEdit().setFocus()
self.entryForm.ui.teamField.lineEdit().setText("Team ")
self.entryForm.ui.teamField.lineEdit().setSelection(5,1)
Notice there are two spaces after 'Team' and the second one is intentionally selected. Then the very next keypress will overwrite that second space; that is basically the desired behavior.
Anyway it looks like something bizarro with the selection scheme; one way to look at this is that the highlight isn't really a selection, but, if you set a valid real selection then it will override the original highlighted 'pseudo-selection'. The original highlighting behaves like a selection in that a keypress will replace everything that's highlighted, but, not like a selection in that the selection methods reveal that there is no 'selection', see UPDATE2 in the original post.
Can anyone help explain this behavior? I'd like to build some more confidence in it before accepting this coincidental answer.
Thanks

Ruby: File dialog wont close

I am using the ruby gem gtk2 to display a simple file dialog, where the user can select a file from his computer. It works just fine, however there is one little problem.
The file dialog doesnt close after selecting the file, but it stays open until the whole script finished.
My file dialog looks like this:
def ask_for_file(question)
dialog = Gtk::FileChooserDialog.new(question, nil,
Gtk::FileChooser::ACTION_OPEN,
"gnome-vfs",
[Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT],
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL]
)
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
file_name = dialog.filename.to_s
return file_name
else
return nil
end
end
When I call it in my script like this:
path = ask_for_file("Test?")
sleep(5)
puts "continue with #{path}"
The file dialog stays open for 5 seconds and is unresponsive in that time. How could I get it to close after a file has been selected, but before the 5 second sleep?
I tried to use dialog.destroy in ask_for_file just before returning the file_name but that didnt seem to help.
I solved it by creating a new window with a button to open the file dialog. Upon clicking the open button, I also send a signal to destroy the main window. This way everything closes immediatly after I selected a file.
For reference see this example
You need to manually call gtk_widget_destroy() on GTK+-provided dialog boxes like GtkFileChooserDialog; clicking on one of the buttons won't be enough. Note that this destroys the file chooser part of the dialog as well, so be sure to get your filename and anything else of value out before you do.

Ruby with Qt

I'm working on a multi form Ruby-Qt program using, and I'm having a problem with controlling secondary windows from the primary one.
How can i disable the primary window when any secondary one is open, also how to take a secondary window output to use it on the primary one, and finally, sorry this is a silly one, what is the appropriate method for closing any window (like this.close in .net) ????
You can make a dialog modal, this disables user interaction with other windows of your application until the user closes the modal window. Use Qt::Dialog.exec instead of Qt::Dialog.show to pop up the window as a modal dialog. This method returns Qt::Dialog::Accepted or Qt::Dialog::Rejected depending on how the user closed the dialog.
To use data from a dialog in the main application window, just save the data somewhere in the dialog class where the main program can access it. For example:
class MyDialog < Qt::Dialog
attr_reader :data
[...]
def updateData(new)
#data = new
end
end
dlg = MyDialog.new(self)
if (dlg.exec == Qt::Dialog::Accepted)
#aButton.text = dlg.data
end
If you are using a dialog, you need to exit it with accept() or reject(), in most cases these are connected to the OK and Cancel button:
connect(okButton, SIGNAL('clicked()'), self, SLOT('accept()'))
connect(cancelButton, SIGNAL('clicked()'), self, SLOT('reject()'))
Other windows can be closed with the close() method.

Removing a slot on a child button click

I've been trying to remove a slot from a child button click, but I can't seem to get it to work. E.G.
flow do
button("X") {parent.remove}
end
Any suggestions?
Shoes' blocks are sometimes tricky. The key here is to ask yourself, what is the parent method being called upon? self, of course. And self references the app (or window, or dialog), not the button.
There are two similar ways to get around this. First, you can create a reference to the button to use in the block:
flow do
a = button("X") {a.parent.remove}
end
Or, you could just reference the slot itself:
b = flow do
button("X") {b.remove}
end

Resources