I've been having an issue with segmentation faults when accessing a class variable (an array of Qt::Action
s), and I've been trying to whittle it down to a minimal code example that replicates the issue:
#!/usr/bin/ruby
require 'Qt4'
require 'rufus/scheduler'
app = Qt::Application.new(ARGV)
class ManagerWidget < Qt::Widget
signals 'changed(int)'
def initialize
super(nil)
max_index = 2
next_index = 0
scheduler = Rufus::Scheduler.start_new
scheduler.every '10s' do
emit changed(next_index)
if next_index < max_index - 1
next_index += 1
else
next_index = 0
end
end
end
end
class Tray < Qt::Widget
def initialize
super(nil)
tray = Qt::SystemTrayIcon.new
manager_widget = ManagerWidget.new
menu = Qt::Menu.new
tray_icon = Qt::Icon.new("./icons/Gnome-Preferences-Desktop-Wallpaper-64.png")
actions = []
manager_widget.connect(SIGNAL('changed(int)')) do |i|
puts "changed #{i}"
actions[i].text = '...' if actions && actions[i]
end
2.times do |i|
sub_menu = Qt::Menu.new("#{i + 1}")
actions[i] = sub_menu.add_action('x')
menu.add_menu(sub_menu)
end
tray.icon = tray_icon
tray.context_menu = menu
tray.show
end
end
Tray.new
app.exec
The above code outputs:
"sni-qt/12147" WARN 14:35:48.326 void StatusNotifierItemFactory::connectToSnw() Invalid interface to SNW_SERVICE
changed 0
changed 1
changed 0
changed 1
changed 0
changed 1
changed 0
changed 1
./min-code-example.rb:42: [BUG] Segmentation fault
ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-linux]
[... snipped due to character limits on posting ...]
[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html
Aborted (core dumped)
Failed (134)
It seems to be necessary to use the sub-menus to trigger this issue - at least adding the actions to the tray menu itself runs for quite a while with no issue (I left it for more than 10 times as long as it took to get the error with the above code).
Ruby version: ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-linux]
Qt version: 4.8.1
Bindings version: qtbindings (4.8.3.0)
Linux version: Ubuntu 12.04
Another odd behaviour is that the output from the puts
call is sometimes displayed in the console immediately, other times it only prints once I mouse over the tray icon.
I'm not even sure where to start when it comes to tracking this down, so any input you have is much appreciated.
Update: I've left this for a while since I couldn't get any further. I've tried to start over with this but I'm still coming across the same problem. I've delved a little deeper in to the problem and since it said 'missing_method' I started by printing the class to make sure I was definitely looking at a Qt::Action (I am), and then which methods are available according to actions[0].methods
.
.methods
output:
[:setShortcut, :shortcut=, :qobject_cast, :inspect, :pretty_print, :className,
:class_name, :inherits, :findChildren, :find_children, :findChild, :find_child,
:connect, :method_missing, :const_missing, :dispose, :isDisposed, :disposed?,
:qVariantValue, :qVariantFromValue, :**, :+, :~, :-@, :-, :*, :/, :%, :>>,
:<<, :&, :^, :|, :<, :<=, :>, :>=, :==, :is_a?, :kind_of?, :methods,
:protected_methods, :public_methods, :singleton_methods, :qDebug, :qFatal,
:qWarning, :SIGNAL, :SLOT, :emit, :QT_TR_NOOP, :QT_TRANSLATE_NOOP, :nil?, :===,
:=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup,
:initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust,
:untrusted?, :trust, :freeze, :frozen?, :to_s, :private_methods,
:instance_variables, :instance_variable_get, :instance_variable_set,
:instance_variable_defined?, :instance_of?, :tap, :send, :public_send,
:respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method,
:define_singleton_method, :object_id, :to_enum, :enum_for, :equal?, :!, :!=,
:instance_eval, :instance_exec, :__send__, :__id__, "blockSignals", "children",
"connect", "deleteLater", "disconnect", "dumpObjectInfo", "dumpObjectTree",
"dynamicPropertyNames", "event", "eventFilter", "inherits", "installEventFilter",
"widgetType?", "killTimer", "metaObject", "moveToThread", "objectName", "parent",
"property", "qt_metacall", "qt_metacast", "removeEventFilter", "objectName=",
"parent=", "setProperty", "setUserData", "signalsBlocked", "startTimer",
"thread", "userData", "actionGroup", "activate", "associatedGraphicsWidgets",
"associatedWidgets", "autoRepeat", "data", "font", "hover", "icon", "iconText",
"checkable?", "checked?", "enabled?", "iconVisibleInMenu?", "separator?",
"visible?", "menu", "menuRole", "parentWidget", "priority", "actionGroup=",
"autoRepeat=", "checkable=", "checked=", "data=", "disabled=", "enabled=",
"font=", "icon=", "iconText=", "iconVisibleInMenu=", "menu=", "menuRole=",
"priority=", "separator=", "shortcut=", "shortcutContext=", "shortcuts=",
"softKeyRole=", "statusTip=", "text=", "toolTip=", "visible=", "whatsThis=",
"shortcut", "shortcutContext", "shortcuts", "showStatusText", "softKeyRole",
"statusTip", "text", "toggle", "toolTip", "trigger", "whatsThis"]
In this case I was trying to do actions[0].enabled = true
when the segmentation fault occurred, which is in the output as far as I understand it (6th line from the bottom, enabled=
). I also tried setEnabled(true)
, set_enabled(true)
and just about anything I could think of.
Even calling inspect
on the action object is causing a segmentation fault, though doing this inside the loop that initially places the actions in the array is fine. I really don't get where this is going wrong.
Edit: In response to the suggestion to use QTimer
instead I've attempted this and I'm still getting the same problem. The code is currently a fair bit more complicated than what is listed above, so I'll write some quick pseudo-code here to illustrate the flow:
def init_tray
actions = []
2.times do |i|
actions[i] = ... # init and add to systray, store reference in array
end
update = Proc.new do |i|
actions[i].setText('...') # update the text on one of the actions defined above
end
listener = ... # create a listener that can be called by Manager on certain events - will call the update Proc among other things.
manager = Manager.new(..., listener)
end
The call to Manager.new initialises that object and at the end calls the listener with a state change, this in turn calls the update
Proc, which accesses the actions
array. This should all be in the same thread at this stage, none of these lines depend on QT apart from the actual creation of the Qt::Action
. I've removed the Rufus scheduling and replaced it with QTimer
, but it simply isn't getting far enough for that to be the issue.
I've spent far too long on this without being able to work out why this is happening (thanks to those that have tried to help, but it has unfortunately not solved the issue). As such, I'm placing my work-around here as an answer in case someone else has a similar issue:
I've done away with the array, instead wrapping the code that was to use it in a Proc
that I create inside the loop that was previously populating the array. Since a Proc
created within this loop has direct access to the variable in question, there is no longer any need to store it in an array and retrieve it later. It brought up one or two issues of it's own since the code in the Proc
is now executed once per action per SIGNAL
rather than having one code block going through all relevant actions, but they were significantly easier to handle.