As a MacRuby beginner I am working through this tutorial, and want to add a drop-down sheet to warn when the user tries to delete an entry from the app.
Following the code here, which in Obj-C is
- (IBAction)deleteRecord:(id)sender
{
NSString *title = #"Warning!";
NSString *defaultButton = #"Delete";
NSString *alternateButton = #"Don't Delete";
NSString *otherButton = nil;
NSString *message = #"Are you sure you want to delete the selected record(s)?";
if ( [tableView numberOfSelectedRows] == 0 )
return;
NSBeep();
NSBeginAlertSheet(title, defaultButton, alternateButton, otherButton, mainWindow, self, #selector(sheetDidEnd:returnCode:contextInfo:), nil, nil, message);
}
I have in MacRuby:
def removeFriend(sender)
return if #friendsTableView.numberOfSelectedRows == 0
title = 'Warning!'
defaultButton = 'Delete'
alternateButton = 'Don\'t Delete'
otherButton = nil
s = #friendsTableView.numberOfSelectedRows > 1 ? 's' : ''
message = "Are you sure you want to delete the selected record#{s}?"
NSBeginAlertSheet(title, defaultButton, alternateButton, otherButton, #mainWindow, self, :'alertDidEnd:returnCode:contextInfo:', nil, nil, message)
end
and for alertDidEnd:returnCode:contextInfo:
def alertDidEnd(sheet, returnCode:rCode, contextInfo:cInfo)
<array handling code>
end
When this runs I get a drop-down sheet when the button linked to removeFriend is clicked, but then if I click "Delete" my app crashes with the following error:
unknown: [BUG] unknown Objective-C immediate: 0x1 (nil)
MacRuby 0.12 (ruby 1.9.2) [universal-darwin10.0, x86_64]
(lldb)
Am I doing something wrong with the way the didAlertEnd method is implemented, or is this actually a bug?
Apparently a MacRuby bug: http://www.macruby.org/trac/ticket/1368
Related
I want to be able to call specific phrases by their assigned numbers in this JSON file, so when i call go(1) for example, it displays only the text that has 'Num' set as 1.
my JSON file:
[
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST1"},
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST2"},
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST3"},
{"Num":1, "Name":"Afely", "Emotion":"Neutral", "Text":"2TEST1"}
]
The Textbox code:
extends ColorRect
export var dialogPath = ""
export(float) var textSpeed = 0.005
var dialog
var phraseNum = 0
var finished = false
func go(phraseNum):
$Timer.wait_time = textSpeed
dialog = getDialog()
assert(dialog, "Dialog not found")
nextPhrase()
var f = File.new()
var img = dialog[phraseNum]["Emotion"] + ".png"
$Portrait.texture = load(img)
func _unhandled_input(event):
if event is InputEventKey:
if event.pressed and event.scancode == KEY_Q:
if finished:
$NEXT.play()
nextPhrase()
else:
$Text.visible_characters = len($Text.text)
func getDialog() -> Array:
var f = File.new()
assert(f.file_exists(dialogPath), "File path does not exist")
f.open(dialogPath, File.READ)
var json = f.get_as_text()
var output = parse_json(json)
if typeof(output) == TYPE_ARRAY:
return output
else:
return []
func nextPhrase() -> void:
if phraseNum >= len(dialog):
queue_free()
return
finished = false
$Name.bbcode_text = dialog[phraseNum]["Name"]
$Text.bbcode_text = dialog[phraseNum]["Text"]
$Text.visible_characters = 0
while $Text.visible_characters < len($Text.text):
$Text.visible_characters += 1
$TEXT_AUDIO.play()
$Timer.start()
yield($Timer, "timeout")
finished = true
phraseNum += 1
return
how I call it:
$TextBox.show()
$TextBox.go(1)
and lastly, the tutorial I followed for it:
https://www.youtube.com/watch?v=GzPvN5wsp7Y
How would I approach to do this?
What you asked
What you are asking requires quite some extra work. Let us start with the JSON file:
[
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST1"},
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST2"},
{"Num":0, "Name":"Afely", "Emotion":"Neutral", "Text":"TEST3"},
{"Num":1, "Name":"Afely", "Emotion":"Neutral", "Text":"2TEST1"}
]
This will parse as an Array (everything between [ and ]), where each element is a Dictionary (which are the ones between { and }). This means that you are going to need to iterate over the Array, check every Num.
Before we do that, we need to acknowledge that we would be using the name phraseNum to representing two things:
The index in the dialog Array
The desired value of Num
We are in this situation because of the source material you are using. They have phraseNum as a parameter (here: func go(phraseNum)) that hides a phraseNum field (here var phraseNum = 0 ).
This hinders communication. If I tell you do this with phraseNum, which one is it? That is bound to give us trouble.
I'll be rewriting go so it takes a Num instead of the index in the dialog Array, so I'll keep phraseNum for the index in the dialog Array, and have a different name for the parameter of go.
Let us begin rewriting go:
func go(num):
pass
Now, let us get all the dialogues:
func go(num):
dialog = getDialog()
We are going to iterate over them. Since I will want an index for phraseNum, I will iterate using index:
func go(num):
dialog = getDialog()
for index in dialog.size():
pass
And we need to check if Num matches. If it does, we got our index:
func go(num):
dialog = getDialog()
for index in dialog.size():
if num == dialog[index]["Num"]:
phraseNum = index
break
We need to handle the case where we didn't find it. Now, hmm… The source material only has an assert, I'll keep that approach. So we need a way to know the code didn't find it…
func go(num):
var found := false
dialog = getDialog()
for index in dialog.size():
if num == dialog[index]["Num"]:
phraseNum = index
found = true
break
assert(found, "Dialog not found")
Next the you call nextPhrase(), sure:
func go(num):
var found := false
dialog = getDialog()
for index in dialog.size():
if num == dialog[index]["Num"]:
phraseNum = index
found = true
break
assert(found, "Dialog not found")
nextPhrase()
And then a not used var f = File.new(), I'll not add that.
And you set the portrait texture. Sure:
func go(num):
var found := false
dialog = getDialog()
for index in dialog.size():
if num == dialog[index]["Num"]:
phraseNum = index
found = true
break
assert(found, "Dialog not found")
nextPhrase()
var img = dialog[phraseNum]["Emotion"] + ".png"
$Portrait.texture = load(img)
And I had skipped over the timer thing, I'll insert it now:
func go(num):
var found := false
dialog = getDialog()
for index in dialog.size():
if num == dialog[index]["Num"]:
phraseNum = index
found = true
break
assert(found, "Dialog not found")
$Timer.wait_time = textSpeed
nextPhrase()
var img = dialog[phraseNum]["Emotion"] + ".png"
$Portrait.texture = load(img)
Something else
Now, you said you want only the phrases with the given Num. This is open to interpretation.
To be clear, the code above will have the dialog start at the first instance of the Num you ask for. But it will not end - nor skip - when it finds a different Num. I don't know if you want that or not.
And we have a couple ways to do this. We could remember what was the num and check against it in nextPhrase. I really don't want to do that. So, I will give you an alternative approach: let us make a dialog array that only contains the elements we want.
It looks like this:
func go(num):
var every_dialog = getDialog()
dialog = []
for candidate in every_dialog:
if num == candidate["Num"]:
dialog.append(candidate)
assert(dialog, "Dialog not found")
phraseNum = 0
$Timer.wait_time = textSpeed
nextPhrase()
var img = dialog[phraseNum]["Emotion"] + ".png"
$Portrait.texture = load(img)
Please notice that in this example we are not reading getDialog() to dialog. Instead we are building a dialog array that only contains the entries we want. And we do that by iterating the result of getDialog() (we add to the array with append).
This is a subtle change in the meaning of dialog, because it would no longer represent every entry form the JSON file. Instead it only represent the entries that will be displayed. Before those two things were the same, but they aren't anymore with this change.
What you didn't ask
The function getDialog reads from the JSON file. And you would be doing that every time you call go. You could instead do it once in _ready.
It is important that you understand what each variable represents, and also where you read and write them. Above I mention that the there is a subtle meaning of dialog do to the alternative change. You need to consider that to make this change.
I strongly believe that nextPhrase should handle the timer, and the portrait. There should be no need to set those from go.
I want you to consider this alternative JSON file structure:
[
[
{"Name":"Afely", "Emotion":"Neutral", "Text":"TEST1"},
{"Name":"Afely", "Emotion":"Neutral", "Text":"TEST2"},
{"Name":"Afely", "Emotion":"Neutral", "Text":"TEST3"},
],
[
{"Name":"Afely", "Emotion":"Neutral", "Text":"2TEST1"}
]
]
Then you would get an Array of Arrays, where each element of the nested Array is a Dictionary. Then you can "simply" get the nested array by index, instead of having to iterate over every element.
The structure you get resembles the structure of the JSON file. Which also means you will have to change how you use it. For example, instead of dialog[phraseNum]["Text"] it could be dialog[num][phraseNum]["Text"].
Now consider the source material. In this case we have a node that has both the responsibility of parsing JSON and the responsibility of displaying character dialogues. It would be easier to tinker with this code if these were separated from each other. Presumably the intention of the author is that you would have different JSON files for the different dialogues, so you would switch JSON file when necessary (which also explain why they read the JSON file each time).
But that is not what you asked.
I'm making an A.P.I. that displays dialogue, choices, sprites, audio etc. etc., i was able to do it after looking up this video on youtube but i keep getting this error invalid get '-1' (on base 'array') here's my code so you can see
extends Control
onready var bg_rect = get_node("Media_Sys/BG_Rect")
onready var char_sprite = get_node("Media_Sys/Sprite")
onready var text_box = get_node("Dialog_Sys/Dialog_Text")
onready var text_nam = get_node("Dialog_Sys/Label/Name")
onready var click = get_node("ClickManager")
var player = []
var lu = "Lu"
var ciel = "Ciel"
func _ready():
click.request_ready()
func write(char_name_str, text_str=null):
if text_str == null:
text_str = char_name_str
char_name_str = ""
player.push_front({"type": "dialogue","name": char_name_str, "text": text_str})
func write_component():
text_nam.clear()
text_box.clear()
text_nam.add_text(player[player.size() - 1]["name"])#the debugger says the problem is here
text_box.add_text(player[player.size() - 1]["text"])#as well as here if you remove the first line.
player.pop_back()
func _input(event):
if event is InputEventMouseButton:
write_component()
I have some people say that it's because i didn't define the size of the array, i have some people say that it's really weird that my code somehow set the player as an integer. I've check the array docs page for this and came up with nothing, I've changed some minor things in the code but it still churns out the same invalid error.
extends Node
onready var g = get_parent()
var lu = "Lu"
var ciel = "Ciel"
func _ready():
g.write(lu, "hello")
One thing to note is that it does display the dialogue and name, i just get the error immediantly afterwards.
You initialize the player variable to be an empty list. Then, in write_component you attempt to access this list with the calculated index [player.size() - 1]. The issue is that if player is empty, this ends up as [0-1] or [-1] as an index, which is not valid in GDScript. Your code assumes Write will be called first (and it may, some of the time) so that player is not empty by the time write_component is called.
The most immediate solution is to check if player.size() == 0 before attempting to access the last element.
func write_component():
text_nam.clear()
text_box.clear()
if player.size() > 0:
text_nam.add_text(player[player.size() - 1]["name"])
text_box.add_text(player[player.size() - 1]["text"])
player.pop_back()
So here is my problem, i have been following the github readme for app engine users in order to implement stripe into my app but the thing is that i can’t make it work as it seems that the http.DefaultTransport and http.DefaultClient are not available in App Engine.
I have seen that in the readme you show us how to initialize the Stripe client with app engine but i cannot find any charge card example so this is why i have come to this implementation.
I am used to this problem, since i have been working with app engine for a long time but for some reason i still get this akward error :
cannot use stripe.BackendConfiguration literal (type stripe.BackendConfiguration) as type stripe.Backend in assignment: stripe.BackendConfiguration does not implement stripe.Backend (Call method has pointer receiver)
Here is the code :
func setStripeChargeClient(context context.Context, key string) *charge.Client {
c := &charge.Client{}
var b stripe.Backend
b = stripe.BackendConfiguration{
stripe.APIBackend,
"https://api.stripe.com/v1",
urlfetch.Client(context),
}
c.Key = key
c.B = b
return c
}
Getting the error on that b assignation …
What i can’t figure it out is why this example seems to work all around the web and does not work in my app, if you could please enlighten me on this one, i would be in your debt hahaha
and then calling it this way
stripeClient := setStripeChargeClient(context, "sk_live_key »)
The error message tells you the problem. The stripe.BackendConfiguration type does not implement stripe.Backend interface. The error message also gives the helpful hint that the missing Call method is on the pointer receiver.
The fix is to use a pointer value:
b = &stripe.BackendConfiguration{ // note the &
stripe.APIBackend,
"https://api.stripe.com/v1",
urlfetch.Client(context),
}
See the specification regarding method sets. The method set of a value receiver does not include the pointer receiver methods.
Ok finally got the solution by doing so :
func setStripeChargeClient(context context.Context, key string) *charge.Client {
c := &charge.Client{}
//SETTING THE CLIENT WITH URL FETCH PROPERLY
fetch := &http.Client{
Transport: &urlfetch.Transport{
Context: context,
},
}
//USE SetHTTPClient method to switch httpClient
stripe.SetHTTPClient(fetch)
var b stripe.Backend
b = &stripe.BackendConfiguration{stripe.APIBackend,
"https://api.stripe.com/v1",fetch}
c.Key = key
c.B = b
return c
}
This was my solution as per Stripe's GAE recommendations:
func myHandler(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
myStripeKey := "pk_test_ABCDEF..."
httpClient := urlfetch.Client(c)
stripeClient := client.New(myStripeKey, stripe.NewBackends(httpClient))
chargeParams := &stripe.ChargeParams{
Amount: uint64(totalCents),
Currency: "usd",
Desc: description,
Email: email,
Statement: "MyCompany",
}
chargeParams.SetSource(token)
charge, chargeErr := stripeClient.Charges.New(chargeParams)
var songList = NSMutableArray()
var player = AVPlayer()
var isSelected = false
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var songs = MPMediaQuery() // also tried var songs = MPMediaQuery.songsQuery()
var localSongs: NSArray = songs.items
songList = NSMutableArray(array: localSongs)
tableView.reloadData()
var song: MPMediaItem = songList[0] as MPMediaItem // <<- Error here
var currentItem = AVPlayerItem(URL: song.valueForProperty(MPMediaItemPropertyAssetURL) as NSURL)
player.replaceCurrentItemWithPlayerItem(currentItem)
player.play()
var songTitle: NSString = song.valueForProperty(MPMediaItemPropertyTitle) as NSString
songName.text = songTitle
var time = Float(CMTimeGetSeconds(player.currentItem.duration))
sliderOutlet.value = time
}
I'm building a music player, but the app crasher when i run in the simulator and gives this error: index 0 beyond bounds for empty array, and says this when i try run on iPhone : {
MediaLibrary} Database validation succeeded
fatal error: unexpectedly found nil while unwrapping an Optional value
EDIT: Seems like it's not accessing the local music library on my iPhone and adding songs to the array.
You're accessing an element without testing its size. So songList[0] is out of bounds, hence the crash.
If your media query returns no items, then localSongs and songlist will both be arrays of size 0. When you then try to reference a song with songList[0] you will be accessing index 0 in an empty array. Hence the crash.
Seems more like a problem with expecting the simulator to have music in it. Since it doesn't, songList.count == 0, which means songList[0] should generate an exception. Seems like the expected and desired behavior to me.
Put a check on count using if condition. You are accessing index which can be nil. If accessed it will through runtime error.
Preferred approach would it be wrapped explicitly with ?
So im grabbing a property from core data, using a dictionary. And it grabs it just fine. However, when I try to take that single returned attribute and assign it to the navigationitem.title it throws some nasty error. Am I missing something here..
Its not actually outputting an error message in the console but instead is giving me this:
Thread 1: EXC_BREAKPOINT(code+exc_I386_BPT.....
on this line.
0x10707a5f3: nopw %cs:(%rax,%rax)
Also its saying
0 swift_dynamicCastObjCClassUnconditional
code:
override func viewDidLoad() {
super.viewDidLoad()
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext
let frequest = NSFetchRequest(entityName: "Round")
frequest.propertiesToFetch = NSArray(object: "course")
frequest.returnsObjectsAsFaults = false
frequest.returnsDistinctResults = true
frequest.resultType = NSFetchRequestResultType.DictionaryResultType
var fetchedArray = context.executeFetchRequest(frequest, error: nil)
//this is the line that throw the error...why won't it let me set the title to the array of the single returned result?
self.navigationItem.title = fetchedArray[0]
println(fetchedArray)
}
Without a specific error message you are receiving, this is just a guess...
NSArray un untyped contents (that is to say that elements are of type AnyObject), so the compiler probably doesn't think you can set AyObject as a string. So likely need something like:
self.navigationItem.title = fetchedArray[0] as String
Or:
self.navigationItem.title = String(fetchedArray[0])
To start, I would check couple of things to see if the problem is in the fetched array or in the navigation item:
what exactly is returned in fetchedArray[0]? Is is a string? A nil? Something else? Does it actually have anything at index 0? After checking if fetchedArray is not nil and has any elements, you could use "\(fetchedArray[0])"
What is the state of navigationItem? Is it actually instantiated at the time you are trying to set it's title property? I would suggest checking it for nil. You can also try assigning it a string like this: self.navigationItem.title = "Hello" and see if it fails.