Callbacks of Gtk Radio Buttons in Julia - callback

I'm trying to revive and old code of mine, which used radio buttons in Julia. However, callbacks don't seem to work (similar callbacks to checkboxes and buttons do work). Here's a minimal example:
using Gtk
function test()
win = GtkWindow("Radio Button Test")
vbox = GtkBox(:v)
push!(win, vbox)
choices = ["first", "second", "third", "fourth"]
radios = [GtkRadioButton(choice) for choice in choices]
set_gtk_property!(radios[1], :active, true)
for r in radios
set_gtk_property!(r, :group, radios[1])
signal_connect(r, "toggled") do _
warn_dialog("Changed to: $(choices[findfirst(radios, r)])")
end
push!(vbox, r)
end
showall(win)
nothing
end
What am I doing wrong?
UPDATE: I have updated to the latest version (Julia 1.4.1 with Gtk 1.1.3), but the problem persists, i.e., the callback function does not seem to be called when I click on the radio buttons.

This was not a real issue - the problem was with the callback function: findfirst cannot be used like this anymore, so there was an (invisible) error.
Changing the callback to
warn_dialog("Changed to: $(get_gtk_property(r, :label, AbstractString))")
it works as expected.

Related

Mutually exclusive radio buttons for MacOS in Swift

I've got five radio buttons, and selecting one should deselect the others.
I've been over a lot of the questions here about radio buttons in Swift, but they're either for iOS or outdated versions of Swift, because Xcode isn't offering me options like ".isSelected". I've got ".isEnabled" but clearly semantics matter here, because "enabled" isn't the same thing as "selected" and it shows.
Writing my code as a series of "if-else" statements along these lines:
func disableUnselectedButtons() {
if Button2.isEnabled == true {
Button1.isEnabled = false
Button3.isEnabled = false
Button4.isEnabled = false
Button5.isEnabled = false
}
}
results in a situation where I can select all five buttons, and can't DEselect any of them after another has been selected. I've tried variations of .on/.off as well, and can't find the right one for this situation.
It's also clumsy as heck to write a method with five if-else statements along those lines. So there's that.
What's the best way to go about implementing this?
If your radio buttons have the same superview and have the same action then they should work as expected.
To set the same action for each of your radio buttons you can do one of the following.
If you are using Storyboards, open both storyboard and related NSViewController swift file. Ctrl-drag your first radio button to the swift file. Then do the same for each of the other radio buttons ensuring you are dragging onto the function generated from the first Ctrl-drag.
If you are creating the radio buttons in code then set the action parameter in the init for each radio button to be the same.
Another way to approach this is to represent the buttons as a Set and then it's easy to iterate through them and configure their state. The below code actually allows for allowing multiple selections to support a scenario that wants to "select three of the six options".
let allButtons = Set(1...5). //or however many you want
func selectActiveButtons(_ activeButtons: Set<Int>, from allButtons: Set<Int>){
let inactive = allButtons.subtracting(activeButtons)
setButtonState(forButtons: inactive, isSelected: false)
setButtonState(forButtons: activeButtons, isSelected: true)
}
func setButtonState(forButtons buttons: Set<Int>, isSelected: Bool) {
buttons.forEach{
//remove below line and replace with code to update buttons in UI
print("Button \($0): \(isSelected ? "Selected" : "Unselected")")
}
}
// select buttons 1 & 3.
//If wanting a "classic" radio button group just use one value in the arrayLiteral.
selectActiveButtons(Set(arrayLiteral: 1,3), from: allButtons)

Hide/Show UI elements by MouseOut/MouseOver client handlers

The goal is to mutually replace two elements with each-other by MouseOut/MouseOver events. Specifically the elements are a label and a listbox. There are some UI arrangement in which the implementation works acceptably in Chrome, however it always fails in IE(9). The problem occurs during a selection from the listbox as per browsers ignor the dropped down area as part of the listbox, it triggers the mouseOut handler and hides the listbox.
Is there any solution forcing browsers to consider the listbox together with its dropped down area?
app.createListBox() .setId('listBox');
app.createLabel('Item1') .setId('label')
.addMouseOverHandler(app.createClientHandler()
.forEventSource().setVisible(false)
.forTargets(app.getElementById('listBox')).setVisible(true));
app.getElementById('listBox')
.addItem('Item1')
.addItem('Item2')
.setVisible(false)
.addMouseOutHandler(app.createClientHandler()
.forEventSource().setVisible(false)
.forTargets(app.getElementById('label')).setVisible(true));
Many Thanks
there is a possible workaround using a server handler to hide the listBox. From my tests it behaves quite similarly (if not better ) - you can test it here
function doGet() {
var app = UiApp.createApplication().setStyleAttribute('padding','100px');
var p = app.createVerticalPanel();
var serverHandler = app.createServerHandler('handler').addCallbackElement(p)
var listBox = app.createListBox() .setId('listBox').setName('listBox').addChangeHandler(serverHandler);
var label = app.createLabel('Item1').setId('label')
.addMouseOverHandler(app.createClientHandler()
.forEventSource().setVisible(false)
.forTargets(listBox).setVisible(true));
listBox.addItem('Item1').addItem('Item2').addItem('Item3').addItem('Item4')
.setVisible(false)
p.add(listBox).add(label)
app.add(p)
return app
}
function handler(e){
var app = UiApp.getActiveApplication();
var listBox = app.getElementById('listBox')
var label = app.getElementById('label')
listBox.setVisible(false)
label.setVisible(true).setText(e.parameter.listBox)
return app
}

NSMenu cancelTracking sometimes doesn't cancel tracking

I have a QT app, and I'm using native menus on OSX. I have custom-drawn menu items, which I created by attaching my own NSView-derived class to the NSMenuItems that I want to draw specially. This all works fine; the menu items draw right and activate the menu function correctly. However, after activating the menu function, the menu doesn't go away -- it's still tracking the mouse movement. (The cursor still highlights items) I've spent days googling for answers, and I haven't seen a similar problem elsewhere. My NSView class is simple; I've overridden the "rect" class for drawing, and my mouseUp event is here:
-(void)mouseUp:(NSEvent *)theEvent
{
NSMenuItem* item = [self enclosingMenuItem];
if ( item != nil ){
NSMenu *menu = [item menu];
if ( menu != nil ){
[menu cancelTracking];
[NSApp sendAction:[item action] to:[item target] from:item];
}
}
}
I've also tried using "cancelTrackingWithoutAnimation", and I've tried calling cancelTracking on the parent menuBar. Can anyone tell me under what circumstances "cancelTracking" might fail? I'm not sure what to try next. Thanks.
Its too late but I also faced the same issue and fixed it by using carbon API CancelMenuTracking(),
CancelMenuTracking(
MenuRef inRootMenu,
Boolean inImmediate,
UInt32 inDismissalReason)
Used _NSGetCarbonMenu to get the menuref of NSMenu.
menuRef = _NSGetCarbonMenu(myMenu);
CancelMenuTracking(menuRef,YES,kHIMenuDismissedByCancelMenuTracking); for 10.5 and CancelMenuTracking(menuRef,YES,0); for 10.6 and above

Google's autocomplete not activated by pasting with mouse

The Google autocomplete API doesn't seem to be activating by pasting content into a text input with the mouse. It works fine if involving the keyboard at all, but not with just mouse.
I did notice, however, that after you paste your content into the text input it will activate from almost any keypress (tested right arrow key, end key, space).
You can repro it here on their autocomplete demo site.
Is this a bug? or as designed? If it's as designed, how to apply workaround?
I've got this as a workaround so far, but no simulated keypress events seem to work.
$('.txtLocation').bind("paste", function (e)
{
$('.txtLocation').focus();
var e = jQuery.Event("keydown");
e.keyCode = 39; //39=Arrow Right
$('.txtLocation').trigger(e);
});
It seems this impacts not only the context-menu Paste, but also that of Edit|Paste from the browser menu bar as well as the iOS paste functionality. I've opened a bug with Google. You may wish to "Star" that bug report to catch updates.
I found a workaround that, while a bit of a hack, seems to fix the problem. If you store the pasted value, switch focus on a different field, set the value in the Autocomplete field, and finally focus back on the Autocomplete field things work more or less as expected. Also, you have to do this in a setTimeout() callback - the delay time doesn't seem to matter at all, but if you just do this inline you won't see the expected results.
Here's a code sample of what I'm describing above:
$("#address_field").on("paste", googleMapsAutocompletePasteBugFix);
googleMapsAutocompletePasteBugFix = function() {
return setTimeout(function() {
var field, val;
field = $("#address_field");
val = field.val();
$("#price").focus();
field.val(val);
return field.focus();
}, 1);
};
The last focus() is optional, but the UI is a little less surprising than if you just skipped automatically to the next field.
Following solution seems to work for me (existence of field ending with "address_2" is assumed). Tested on IE8, IE9, IE10, Chrome, FF and Safari
if document.addEventListener
$(document).on("paste", "[name*=address_1]", #googleMapsAutocompletePasteBugFix)
$(document).on("onpaste", "[name*=address_1]", #googleMapsAutocompletePasteBugFix)
else
for element in $("input[name*=address_1]")
document.getElementById($(element).attr('id')).onpaste = #googleMapsAutocompletePasteBugFix
googleMapsAutocompletePasteBugFix: (e) ->
unless e
e = window.event
if e.srcElement
target = e.srcElement
else
target = e.target
field = $(target)
fieldId = field.attr('id')
focusSwitchFieldId = fieldId.replace(/(\d)$/, '2')
setTimeout(->
if window.chrome || /Safari/.test(navigator.userAgent)
val = field.val()
$("##{focusSwitchFieldId}").focus()
field.val(val)
field.focus()
else
field = document.getElementById(fieldId)
val = field.value
document.getElementById(focusSwitchFieldId).focus()
setTimeout(->
field.value = val
field.focus()
field.focus()
, 50)
, 10)

Receiving keystrokes in an opened wx.ComboCtrl

Coming from this question, I have a wxComboCtrl with a custom popup made of a panel with a bunch of radiobuttons.. My problem is that when I open the popup the combo doesn't get keystrokes, because the events get handled by the panel itself.. I'd like to redirect those KeyEvents to the textctrl of the combo, but I can't find a way to get it to work :/
Am I going the wrong way? Should I manually handle the textctrl value as the user presses keys? I think that would be a bit cumbersome though.. Since supposedly the textctrl already knows how to handle those events..
Here's my testcase (wxPython 2.8 on Linux), the "on_key" method should be the culprit:
import wx
import wx.combo
class CustomPopup(wx.combo.ComboPopup):
def Create(self, parent):
# Create the popup with a bunch of radiobuttons
self.panel = wx.Panel(parent)
sizer = wx.GridSizer(cols=2)
for x in range(10):
r = wx.RadioButton(self.panel, label="Element "+str(x))
r.Bind(wx.EVT_RADIOBUTTON, self.on_selection)
sizer.Add(r)
self.panel.SetSizer(sizer)
# Handle keyevents
self.panel.Bind(wx.EVT_KEY_UP, self.on_key)
def GetControl(self):
return self.panel
def GetAdjustedSize(self, minWidth, prefHeight, maxHeight):
return wx.Size(200, 150)
def on_key(self, evt):
if evt.GetEventObject() is self.panel:
# Trying to redirect the key event to the combo.. But this always returns false :(
print self.GetCombo().GetTextCtrl().GetEventHandler().ProcessEvent(evt)
evt.Skip()
def on_selection(self, evt):
self.Dismiss()
wx.MessageBox("Selection made")
class CustomFrame(wx.Frame):
def __init__(self):
# Toolbar-shaped frame with a ComboCtrl
wx.Frame.__init__(self, None, -1, "Test", size=(800,50))
combo = wx.combo.ComboCtrl(self)
popup = CustomPopup()
combo.SetPopupControl(popup)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(combo, 0)
self.SetSizer(sizer)
self.Layout()
if __name__ == '__main__':
app = wx.PySimpleApp()
CustomFrame().Show()
app.MainLoop()
Edit:
I found these (unresolved) discussions on the same topic..
"ComboCtrl loses keyboard focus when ComboPopup is shown "
"issue using wx.ComboCtrl"
I had the same problem with a custom ComboPopup control using a panel as the basis of the control.
EmulateKeypress just entered a never-ending loop. I found binding the panel to the EVT_KEY_DOWN and EVT_CHAR to the overridden OnComboKeyEvent function and skipping the event down the event handling chain was all that was required to enable entering text into the textbox of the ComboCtrl with the popup open. I think this makes sense. As the Popup isn't strictly a control its events won't belong anywhere until bound into the the wx.app event loop through the Bind() method.
I've included the overridden OnComboKeyEvent function below so that anyone else can explore this problem.
def create(self, *args, **kwds):
...
self.panel.Bind(wx.EVT_CHAR, self.OnComboKeyEvent)
self.panel.Bind(wx.EVT_KEY_DOWN, self.OnComboKeyEvent)
...
def OnComboKeyEvent(self, evt):
evt_dict = {wx.EVT_TEXT.typeId: "TextEvent",
wx.EVT_CHAR.typeId:"CharEvent",
wx.EVT_CHAR_HOOK.typeId:"CharHookEvent",
wx.EVT_KEY_UP.typeId:"KeyUpEvent",
wx.EVT_KEY_DOWN.typeId:"KeyDownEvent"}
if evt.GetEventType() in evt_dict:
thelogger.debug("oncombokeyevet:%s" % evt_dict[evt.GetEventType()])
super().OnComboKeyEvent(evt)
evt.Skip()
I've got an answer from Robin Dunn himself on wxpython-users:
Sending low-level events to native
widgets like this does not work the
way that most people expect it to.
You are expecting this to result in
the character being added to the text
ctrl,but it can't do that because all
that ProcessEvent does is send the
wx.Event to any bound event handlers
and then it stops. It does not go to
the next step and convert that event
to the equivalent native message and
send it on to the native widget. See
the "Simulating keyboard events"
thread.
It doesn't work especially well on
non-Windows platforms, but you could
try calling EmulateKeypress instead.