Managing NSWindows using controllers in RubyMotion - rubymotion

How should you keep reference to an NSWindow from a controller? Closing the window will cause it to be deallocated and leaving the controller with a variable pointing to bad memory.
Essentially, this is the specification for the behavior:
describe "Opening and closing NSWindow" do
before do
#app = NSApplication.sharedApplication
#controller = MyWindowController.new
end
it "opens and closes the window" do
#controller.open
#app.windows.length.should == 1
#controller.close
#app.windows.length.should == 0
end
it "can reopen a new window" do
#controller.open
#controller.close
#controller.open # Crashes
#app.windows.length.should == 1
end
it "doesn't care how many times new windows are reopened" do
100.times do
#controller.open
#controller.close
end
#app.windows.length.should == 0
end
it "releases windows when closed" do
#controller.open
#controller.close
#controller.window.should == nil
end
end
Crashing implementation:
class MyWindowController
def open
#window = NSWindow.alloc.initWithContentRect(
NSMakeRect(200, 200, 200, 200),
styleMask: NSTexturedBackgroundWindowMask,
backing: NSBackingStoreBuffered,
defer: false
)
#window.orderFrontRegardless
end
def close
#window.close
end
def window
#window
end
end

class MyWindowController < UIViewController
def viewDidLoad
super.tap do
self.view.addSubview(myWindow)
end
end
def myWindow
#myWindow ||= NSWindow.alloc.initWithContentRect(
NSMakeRect(200, 200, 200, 200),
styleMask: NSTexturedBackgroundWindowMask,
backing: NSBackingStoreBuffered,
defer: false
).tap do |window|
window.orderFrontRegardless
end
end
def close
myWindow.close
end
end

Related

Tkinter button doesn't respond (has no mouse over effect)

I'm writing a game that has info that is communicated from client to server and from server to client. One specific (non-playing) client is the monitor, which only displays the game board and players. This works fine, the only thing that doesn't work is the quit button on the GUI. A minor thing, but I would like it to work. :) Plus I think that there might be something pretty wrong with the code, even though it works.
I tried all kind of different commands (sys.exit, quit...) and nothing fixed it.
There's no error message, nothing happens with the button at all. No mouse over effect, nothing if I click it. Relevant code (I removed matrix and server logic because I think it's irrelevant - if it isn't I'll post it):
class Main():
def __init__(self, master):
self.frame = Frame(master)
self.frame.pack()
# Has to be counted up by server class
rounds = 0
# Has to be communicated by server class. If numberwin == numberrobots,
# game is won
numberwin = 0
numberrobots = 2
def draw(self):
if hasattr(self, 'info'):
self.info.destroy()
if hasattr(self, 'quit'):
self.quit.destroy()
print "Main should draw this matrix %s" % self.matrix
[...] lots of matrix stuff [...]
# Pop-Up if game was won
# TODO: Make GUI quittable
if self.numberwin == self.numberrobots:
self.top = Toplevel()
self.msg = Message(self.top, text="This game was won!")
self.msg.pack(side=LEFT)
self.quittop = Button(
self.top, text="Yay", command=self.frame.destroy)
self.quittop.pack(side=BOTTOM)
# TODO: Quit GUI
self.quit = Button(self.frame, text="Quit", command=self.frame.destroy)
self.quit.pack(side=BOTTOM)
# Information on the game
self.info = Label(
self.frame, text="Rounds played: {}, Numbers of robots in win condition: {}".format(self.rounds, self.numberwin))
self.info.pack(side=TOP)
def canvasCreator(self, numberrows, numbercolumns):
# Game board
self.canvas = Canvas(
self.frame, width=numbercolumns * 100 + 10, height=numberrows * 100 + 10)
self.canvas.pack()
class Agent(Protocol, basic.LineReceiver):
master = Tk()
main = Main(master)
# So first matrix is treated differently from later matrixes
flagFirstMatrix = 1
def connectionMade(self):
msg = dumps({"type": "monitor"})
self.sendLine(msg)
print "Sent message:", msg
def dataReceived(self, data):
# Decode the json dump
print "Data received: %s" % data
data = loads(data)
self.main.matrix = np.matrix(data["positions"])
self.main.goals = np.matrix(data["goals"])
self.main.next_move_by_agent = data["next_move"]
self.main.rounds = data["rounds"]
self.main.numberwin = data["win_states"]
if self.flagFirstMatrix == 1:
self.main.numberrows, self.main.numbercolumns = self.main.matrix.shape
self.main.canvasCreator(
self.main.numberrows, self.main.numbercolumns)
self.main.canvas.pack()
self.flagFirstMatrix = 0
self.main.canvas.delete(ALL)
self.main.draw()
self.master.update_idletasks()
self.master.update()
First there is no indentation for class Agent, second for the quit button's "call back" self.frame.destroy is never defined so it doesn't do anything. If you meant tkinter destroy method try self.frame.destroy() or try explicitly defining it. You can also try calling either fram.pack_forget() or fram.grid_forget()
Add master.mainloop() to your last line in terms of the entire lines of code

Why does set_position fail in this code

When I build this same ui in Glade (the program) the resulting code is flawless, when I code it by hand everything works except one thing, the set_position(670) is ignored and the resulting window comes up at about 1" square on my screen.
My goal is to get the left hand pane to instanciate at 670 px wide. I am not concerned about locking the width.
I have tried placing the call to set_position(670):
where it is shown now
2 lines further down
After the self.top.show_all()
Just before the self.top.show_all()
Just after call to pack1
Searching google has lead to no usefull results, and there are no useful answers here that I have found so far.
The pyGtk site indicates that the call can be placed almost anywhere.
The gtk site lists no restrictions.
Most things I have found seem to indicate it is due to the resize/shrink attributes of one of the other widgets, but after numerous attempts at altering the values of everything in the left pane, I am still at a loss. (Note that value changes were tried one or two at a time.)
import gtk
import gobject
class Source(gobject.GObject):
def _emit_signal(self, button, value):
self.emit('choice_changed', self, value)
def __init__(self):
self.__gobject_init__()
# packing treee
# top
# main
# inst_align
# inst_window
# instructions
# opt_align
# opt_buttons
# opt_0 (through opt_5)
# doit_box
# doit
self.top = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
self.main = gtk.HPaned()
self.main.set_position(670)
self.top.add(self.main)
# Build left side components
self.inst_align = gtk.Alignment(0.0, 0.0, 1.0, 1.0)
self.inst_window = gtk.ScrolledWindow(None, None)
self.inst_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
self.instructions = gtk.TextView(None)
self.instructions.get_buffer().set_text(
"Very long (1200 char) string snipped.")
#pack pane 1
self.inst_window.add(self.instructions)
self.inst_align.add(self.inst_window)
self.main.pack1(self.inst_align, True, False)
# build right side components
self.opt_align = gtk.Alignment(1.0, 0.0, 0.0, 0.0)
self.opt_buttons = gtk.VButtonBox()
self.opt_buttons.set_layout(gtk.BUTTONBOX_START)
self.opt_buttons.set_homogeneous(True)
self.opt_0 = gtk.RadioButton(
None,
"Label 0",
True)
self.opt_1 = gtk.RadioButton(
self.opt_0,
"Label 1",
True)
self.opt_2 = gtk.RadioButton(
self.opt_0,
"Label 2",
True)
self.opt_3 = gtk.RadioButton(
self.opt_0,
"label 3",
True)
self.opt_4 = gtk.RadioButton(
self.opt_0,
"Label 4",
True)
self.opt_5 = gtk.RadioButton(
self.opt_0,
"Label 5",
True)
self.doit_box = gtk.HButtonBox()
self.doit = gtk.Button("Do It !", None, True)
self.doit.expand = False
self.doit.fill = False
self.doit.set_alignment(0.5, 0.0)
# Put the buttons in the box and while were at it, bind their
# released, and pressed options
for i in range(0, 6):
s = "self.opt_" + str(i)
self.opt_buttons.pack_start( eval(s), True, False, 0 )
s1 = s + '.connect("released", self._emit_signal, i)'
s2 = s + '.connect("pressed", self._emit_signal, i)'
exec(s1)
exec(s2)
self.doit_box.pack_start(self.doit, True, False, 0)
self.opt_buttons.pack_start(self.doit_box, True, False, 0)
self.opt_align.add(self.opt_buttons)
self.main.pack2(self.opt_align, True, False)
self.top.show_all()
gobject.type_register(Source)
gobject.signal_new('choice_changed',
Source,
gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, (Source, gobject.TYPE_INT))
if __name__ == "__main__":
def cb_test(widget, event, value):
print "widget is", widget
print "Value is ", value
return
test = Source()
win = gtk.Window()
win.add(test.top)
win.show_all()
test.connect('choice_changed', cb_test)
gtk.main()
You have to set a window with greater than the position of the HPane divider. Here is a stripped down example:
import gtk
class Source(gtk.Window):
def __init__(self):
super(Source, self).__init__()
label1 = gtk.Label("The first label")
label2 = gtk.Label("The second label")
paned = gtk.HPaned()
paned.add1(label1)
paned.add2(label2)
paned.set_position(600)
self.add(paned)
self.set_size_request(800, 400) # needs sufficient width here
self.connect('delete-event', gtk.main_quit)
self.show_all()
if __name__ == "__main__":
test = Source()
gtk.main()

How to correctly create a second window in pyqt4

I'm trying to open a second window beside my mainwindow on a button click and display a tablewidget on it with some data. When I open the window it raises
AttributeError: 'Ui_MainWindow' object has no attribute 'openTable'
The mainwindow was created with Qt Designer and converted the ui file with pyuic4. How do I do this correctly correct so that the error is not raised?
The button calls function:
def showCliplist(self):
data = self.metadata_list
luts = self.lutlist
selected_lut = self.LUTBox.currentIndex()
openTable = ClipListViewer(data,luts,selected_lut)
self.openTable.show()
New window class:
class ClipListViewer(QtGui.QWidget):
def __init__(self, data, luts, selected_lut, parent = None):
super(ClipListViewer,self).__init__()
self.setWindowTitle('Cliplist')
self.resize(900,600)
self.metadata = data
self.curentluts = luts
self.choosenlut = selected_lut
y_count = len(self.metadata)
self.table = QtGui.QTableWidget(y_count,6)
self.table.setHorizontalHeaderLabels(['Clip', 'Videocodec', 'FPS', 'Audiocodec', 'Start Timecode', 'LUT'])
x = y = 0
for items in self.metadata:
for entry in items:
#print entry
self.table.setItem(y, x, QtGui.QTableWidgetItem(entry))
self.table.resizeColumnToContents(x)
x += 1
self.comb = QtGui.QComboBox()
for lutname in self.curentluts:
self.comb.addItem(lutname)
self.comb.setCurrentIndex(self.choosenlut)
self.table.setCellWidget(y, 5, self.comb)
self.table.setColumnWidth(5, 230)
y += 1
x = 0
self.table.resizeRowsToContents()
layout = QtGui.QHBoxLayout()
layout.addWidget(self.table)
self.setLayout(layout)
self.show()
def closeEvent(self, event): #check if window was closed
print "Cliplist Window was closed! "
I reviews your code completed, OK, let's me explain.
AttributeError: 'Ui_MainWindow' object has no attribute 'openTable'
This error says, "I not have self.openTable in class Ui_MainWindow (That true because your have create own method)".
Why ? : Because a problem is in line this;
def showCliplist(self):
data = self.metadata_list
luts = self.lutlist
selected_lut = self.LUTBox.currentIndex()
openTable = ClipListViewer(data,luts,selected_lut) # <- (1) THIS LINE
self.openTable.show() # <- (2) THIS LINE
In (1), your create your second widget in to openTable (Not self.openTable).
This line we have this object in openTable (Not self.openTable).
Then (2), your call QtGui.QWidget.show(self) to show widget of self.openTable (Not openTable). It's should be error because we don't have variable self.openTable. To fix it your should use same name variable, Like this;
def showCliplist(self):
data = self.metadata_list
luts = self.lutlist
selected_lut = self.LUTBox.currentIndex()
self.openTable = ClipListViewer(data,luts,selected_lut) # <- FIX THIS LINE !
self.openTable.show() # <- (2) THIS LINE
Why second widget is show before I call self.openTable.show()?
Answer : Your can see in second widget initiate in last line of code your see QtGui.QWidget.show(self) has be call before end of initiate;
class ClipListViewer(QtGui.QWidget):
def __init__(self, data, luts, selected_lut, parent = None):
super(ClipListViewer,self).__init__()
.
.
.
self.show() # <- THIS LINE
Regards,

Pygame midi prevents other input

I wish to have real-time midi input, in order to control some wx.Sliders. I have been able to achieve this, however it prevents interaction with the sliders via the mouse or keyboard and causes the application to crash.
This is the code I have at the moment.
import wx, pygame, pygame.midi
class windowClass(wx.Frame):
def __init__(self, *args, **kwargs):
super(windowClass, self).__init__(*args, **kwargs)
self.basicGUI()
def basicGUI(self):
panel = wx.Panel(self)
self.slider = wx.Slider(panel, -1, 2, 0, 128, pos=(10,25), size=(250,-1), style=wx.SL_AUTOTICKS | wx.SL_LABELS)
sliderText = wx.StaticText(panel, -1, 'Slider 1 ', (8,8))
self.slider2 = wx.Slider(panel, -1, 2, 0, 128, pos=(10,110), size=(250,-1), style=wx.SL_AUTOTICKS | wx.SL_LABELS)
sliderText = wx.StaticText(panel, -1, 'Slider 2', (8,88))
self.Bind(wx.EVT_SLIDER, self.sliderUpdate)
self.SetTitle('Sliders Window!')
self.Show(True)
pygame.init()
pygame.midi.init()
inp = pygame.midi.Input(1)
running = True
while running:
if inp.poll():
dataset = inp.read(1)
control = dataset[0][0][1]
if control > 8:
continue
if control == 1:
value = dataset[0][0][2]
self.slider.SetValue(value)
if control == 2:
value = dataset[0][0][2]
self.slider2.SetValue(value)
pygame.time.wait(10)
def sliderUpdate(self, event):
value1 = self.slider1.GetValue()
value2 = self.slider2.GetValue()
print value1, value2
def main():
app = wx.App()
windowClass(None)
app.MainLoop()
main()
What is causing pygame.midi to take all resources? I have a feeling it is regarding while running = True, however my attempts at trying to close the instance don't seem to work.
How can I have the sliders being controlled by the midi and the mouse calling sliderUpdate? Thanks for any help.
You have a loop that never exits, so your program never reaches the parts that deal with anything except the midi input
I would move the code that is currently in that loop into a function, remove the loop, and add a timer to the panel, e.g.
def basicGUI(self):
... panel stuff
pygame.init()
pygame.midi.init()
timer = wx.Timer(self, -1)
self.Bind(wx.EVT_TIMER, self.OnTimer)
timer.Start(10, False)
def OnTimer(self, event):
inp = pygame.midi.Input(1)
if inp.poll():
dataset = inp.read(1)
control = dataset[0][0][1]
if control > 8:
continue
if control == 1:
value = dataset[0][0][2]
self.slider.SetValue(value)
if control == 2:
value = dataset[0][0][2]
self.slider2.SetValue(value)

Image not working in custom MKOverlayRenderer (RubyMotion)

I have a RubyMotion app with a MKMapView in a controller that I am trying to add an image overlay to.
I'm adding the overlay here (the delegate of the MKMapView instance is set to the controller itself):
image = UIImage.imageNamed("map").CGImage
lowerLeft = CLLocationCoordinate2DMake(21.652538062803, -127.620375523875420)
upperRight = CLLocationCoordinate2DMake(50.406626367301044, -66.517937876818)
overlay = ImageOverlay.alloc.initWithImageData image, withLowerLeftCoordinate:lowerLeft, withUpperRightCoordinate:upperRight
self.view.addOverlay overlay
Here's my custom overlay:
class ImageOverlay
attr_accessor :imageData
attr_accessor :mapRect
def initWithImageData imageData, withLowerLeftCoordinate:lowerLeftCoordinate, withUpperRightCoordinate:upperRightCoordinate
self.imageData = imageData
lowerLeft = MKMapPointForCoordinate(lowerLeftCoordinate)
upperRight = MKMapPointForCoordinate(upperRightCoordinate)
self.mapRect = MKMapRectMake(lowerLeft.x, upperRight.y, upperRight.x - lowerLeft.x, lowerLeft.y - upperRight.y)
return self
end
def coordinate
return MKCoordinateForMapPoint(MKMapPointMake(MKMapRectGetMidX(self.mapRect), MKMapRectGetMidY(self.mapRect)))
end
def boundingMapRect
return self.mapRect
end
end
And here is the custom MKOverlayRenderer:
class ImageOverlayRenderer < MKOverlayRenderer
def drawMapRect mapRect, zoomScale:zoomScale, inContext:context
puts "drawMapRect"
theMapRect = self.overlay.boundingMapRect
theRect = self.rectForMapRect(theMapRect)
CGContextScaleCTM(context, 1.0, -1.0)
CGContextTranslateCTM(context, 0.0, -theRect.size.height)
CGContextDrawImage(context, theRect, self.overlay.imageData)
end
end
And in my view controller I am overriding the mapView:rendererForOverlay method:
def mapView mapView, rendererForOverlay:overlay
if overlay.isKindOfClass(ImageOverlay)
renderer = ImageOverlayRenderer.alloc.initWithOverlay overlay
return renderer
end
return nil
end
The problem is that drawMapRect is never called and the app crashes with no error except stating a crash report may have been generated, which contains this:
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x0000000000000000
Everything else seems to work up until that point, mapView:rendererForOverlay is invoked and I am returning the renderer. I even override canDrawMapRect and it was being invoked.
Any ideas on how to get this working?
The problem was related to bug in RubyMotion which has been fixed and will be made available in the next release:
http://hipbyte.myjetbrains.com/youtrack/issue/RM-533
Update: The fix has been released in RubyMotion 2.31 and I have verified it resolves my issue.