Fortschrittsbalken tut nicht

  • Fortschrittsbalken tut nicht

    Hallo,

    ich brauche mal wieder Eure Hilfe.
    Ich möchte während einer While-Schleife, in welcher eine Berechnung stattfindet, einen Fortschrittsbalken mittels NSProgressIndicator anzeigen.

    Folgender Aufbau:

    in der AppDelegate.h

    Quellcode

    1. NSProgressIndicator *fortschritt;
    2. @property (nonatomic, retain) IBOutlet NSProgressIndicator *fortschritt;


    in der AppDelegate.m

    Quellcode

    1. ...
    2. anzahl_tage = 0;
    3. // In piiinterval stehen die Anzahl der Tage drin. so oft wird die While-Schlife durchlaufen
    4. double piinterval = [ende timeIntervalSinceDate:beginn] / 86400;
    5. [fortschritt setMaxValue:piinterval];
    6. [fortschritt setMinValue:0.0];
    7. [fortschritt setHidden:FALSE];
    8. [fortschritt startAnimation: self];
    9. [fortschritt display];
    10. do {
    11. //irgendeine Berechnung
    12. anzahl_tage++;
    13. [fortschritt setDoubleValue:(double) anzahl_tage];
    14. [fortschritt display];
    15. while(abbruchbedingung);
    16. }
    17. [fortschritt stopAnimation: self];
    18. [fortschritt setHidden:TRUE];
    Alles anzeigen


    Der Fortschrittsbalken wird bei den ersten gesamten Durchläufen der Schelife gar nicht angezeigt. Bei jeden weiteren Durchläufen erst nach dem die Berechnungen abgeschlossen sind und dann auch nicht kontinuierlich, sondern jedesmal anders. Mal beginnt er in der Mitte, mal springt er sofort aufs Ende.

    Was mache ich falsch?

    Danke und Grüße
    Bernd
    Ich bin gegen Signaturen!!!
  • Die Fortschrittsanzeige kann natürlich nicht neu gezeichnet werden, solange Du den Hauptthread, der für das Zeichnen der Views zuständig ist, mit Deiner while-Schleife blockierst. Der Hauptthread sollte nienienie über längere Zeit (ein paar Millisekunden) blockiert werden.

    Das kannst Du so lösen wie thallius schrieb. Es gibt aber noch zig andere Möglchkeiten, z.B. über eine NSOperation. Such im Forum mal nach NSProgressIndicator, das Thema ist hier schon oft diskutiert worden.
    Multigrad - 360°-Produktfotografie für den Mac
  • Danke erstmal für Eure Antworten.

    Ich führe meine Berechnungen nach Buttonpress in - (IBAction)rechnen:(id)sender aus und gebe das Ergebnis in einer TableView aus. Jedesmal, wenn der Button gedrückt wird, startet eine neue Berechnung.

    Wie muss ich das jetzt umbauen? Ich schnalls nicht :(
    Ich bin gegen Signaturen!!!
  • nur weil ich gerade Langeweile habe

    Quellcode

    1. -(IBAction)rechnen:(id)sender
    2. {
    3. [self performSelectorInBackGround:@selector(calcInBG:) withObject:nil]; // Wenn Du Werte aus dieser Methode in der CalcInBG brauchst kannst du diese in ein NSDictionary packen und dieses als Object mitgeben
    4. };
    5. -(void)calcInBG:(NSDictionary*)meineWerte
    6. {
    7. While()
    8. {
    9. rechne…
    10. [self performselectorInMainThread:@selector(updateProgress:) withObject:[NSNumber numberWithInt:ProgressZustand]];
    11. }
    12. }
    13. -(void)updateProgress:(NSNumber*)Zustand
    14. {
    15. stlle progress auf Zustand
    16. }
    Alles anzeigen


    Rest musst du selber schaffen

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • little_pixel schrieb:

    Meinem Wissenstand nach ist dieser thread safe.

    Woher hast Du die Info?

    Nach Threading Programming Guide sind alle NSResponder grundsätzlich thread unsafe, es sei denn, in der spezifischen Klassendokumentation steht etwas anderes. Und das tut es nicht. Ausgehend von der Information kann man erstmal nicht davon ausgehen, dass NSProgressIndicator thread safe ist. Oder hast Du eine andere Quelle?

    Multithreading ist übrigens eine ganz tolle Möglichkeit, sich innerhalb kürzester Zeit große Probleme und ekelhaftes, nicht reproduzierbares Fehlverhalten einzufangen, wenn man nicht genau weiß, was man tut. Besser auf eine höhere Abstraktion gehen oder ganz drauf verzichten. Es gibt kaum Situationen, in denen man rohes Multithreading nicht vermeiden kann - und das will man, wo immer möglich.
    Multigrad - 360°-Produktfotografie für den Mac
  • Tobse001 schrieb:

    Mit dispatch_async / Blocks müsste man das nicht in mehrere Methoden auseinander brechen.


    Sicher, aber ich habe bewußt keine Blocks genommen. Ich finde Blocks sind nämlich alles andere als Anfängerfreundlich. Ich habe mich jedenfalls selten so schwer getan etwas zu kapieren wie diese merkwürdigen Konstrukte. Mal ganz davon abgesehen, dass ich mir die Syntax bis heute nicht merken kann und jedesmal wieder nachsehen muss wie man das mit den Parametern schreibt wenn ich einen Block erzeuge.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Wo liegt jetzt das Problem ?

    Dein Programm läuft grundsätzlich erst einmal in einem Thread: Dem MainThread. Dieser teilt sich seine Zeit mit der RunLoop. Bedeutet: Wenn Dein Programm dran ist, dann darf die Runloop nichts machen. Die Runloop ist aber für alles zuständig was der User so macht. Also Eingaben und Ausgaben. Eben auch alles was mit dem UI zusammen hängt. Läuft nun also Dein Programm in einer Schleife, dann muss die Runloop warten bis Dein Programm damit fertig ist und kann dann erst wieder was machen. Sprich solange deine Schleife läuft wird sich an der UI nichts ändern.
    lso der Ablauf ist ganz einfach. Du sagst z.B. dem Progress Indikator im Mainthread "Ey sachmal der Runloop bescheid du hast neue Werte zum Anzeigen". Das macht der auch. Jetzt muss aber erst die Runloop drankommen und die bekommt dann die Message von dem ProgressIOndikator und malt diesen schließlich neu.

    Damit das nicht so ist startest du jetzt einfach deine Berechnung in einem Hintergrund Thread. also einen zweiten Thread. Dein MainThread startet also nur den BackgroundThread und gibt dann wieder ab an die runloop. Der zweite Thread läuft dann halt mal gemütlich los und berechnet was du berechnen willst. Dieser Background Thread hat aber nun dummerweise keine Verbindung zur Runloop. Ergo muss ich, um im UI was zu ändern, dem MainThread Bescheid sagen: "Ey, änder mal den Progress Indikator und geh dann wieder in die Runloop damit diese die Änderung anzeigt".

    Das Ganze ist jetzt stark vereinfacht und sicher nicht Aminfest, aber eventuell ganz ok um den Einstieg zu bekommen.

    Gruß

    Claus
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)
  • Wenn Du das Source Code Gerüst von Claus verwendest, dann darfst Du in calcInBG: fortschritt nicht verwenden.

    Alles was in Deinem Code mit fortschritt zu tun hat gehört in die Methode rechnen: also etwa so:

    Quellcode

    1. - (IBAction)rechnen:(id)sender
    2. {
    3. double piinterval = [ende timeIntervalSinceDate:beginn] / 86400;
    4. [fortschritt setMaxValue:piinterval];
    5. [fortschritt setMinValue:0.0];
    6. [fortschritt setHidden:FALSE];
    7. [fortschritt startAnimation: self];
    8. [self performSelectorInBackGround:@selector(calcInBG:) withObject:nil]; // Wenn Du Werte aus dieser Methode in der CalcInBG brauchst kannst du diese in ein NSDictionary packen und dieses als Object mitgeben
    9. };
    10. - (void)calcInBG:(NSDictionary*)meineWerte
    11. {
    12. anzahl_tage = 0;
    13. // In piiinterval stehen die Anzahl der Tage drin. so oft wird die While-Schlife durchlaufen
    14. double piinterval = [ende timeIntervalSinceDate:beginn] / 86400;
    15. do {
    16. //irgendeine Berechnung
    17. anzahl_tage++;
    18. [self performSelectorOnMainThread:@selector(updateProgress:) withObject:[NSNumber numberWithInt:anzahl_tage] waitUntilDone:NO];
    19. while(abbruchbedingung);
    20. }
    21. [self performSelectorOnMainThread:@selector(calcCompleted) withObject:nil waitUntilDone:NO];
    22. }
    23. - (void)calcCompleted
    24. {
    25. [fortschritt stopAnimation: self];
    26. [fortschritt setHidden:TRUE];
    27. }
    Alles anzeigen
  • Hallo MCDan, danke erstmal. Aber irgendwie funktioniert das immer noch nicht :( Sollte jetzt alles an der Progressbar hängen?

    Ich poste mal die gesamte .m

    Quellcode

    1. #import "AppDelegate.h"
    2. @implementation AppDelegate
    3. @synthesize window = _window;
    4. @synthesize betrag_eingabe;
    5. @synthesize betrag_ausgabe;
    6. @synthesize basiszins_eingabe;
    7. @synthesize dynzins_eingabe;
    8. @synthesize festzins_eingabe;
    9. @synthesize gesamt_ausgabe;
    10. @synthesize geschaeftsart;
    11. @synthesize beginn_eingabe;
    12. @synthesize ende_eingabe;
    13. @synthesize tage_ausgabe;
    14. @synthesize beginn_ausgabe;
    15. @synthesize ende_ausgabe;
    16. @synthesize zinsen_ausgabe;
    17. @synthesize protag_ausgabe;
    18. @synthesize zinssatz_ausgabe;
    19. @synthesize zinsArray;
    20. @synthesize fortschritt;
    21. - (id)init
    22. {
    23. self = [super init];
    24. if (self) {
    25. zinsArray=[[NSMutableArray alloc]init];
    26. newDic=[[NSMutableDictionary alloc] init];
    27. }
    28. return self;
    29. }
    30. - (IBAction)setMinDate:(id)sender
    31. {
    32. [ende_eingabe setMinDate: [beginn_eingabe dateValue]];
    33. }
    34. - (IBAction)setMaxDate:(id)sender
    35. {
    36. [beginn_eingabe setMaxDate: [ende_eingabe dateValue]];
    37. }
    38. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
    39. {
    40. [ende_eingabe setDateValue: [NSDate date]];
    41. double startbetrag = 1000;
    42. [betrag_eingabe setFloatValue:startbetrag];
    43. [basiszins_eingabe setFloatValue:basiszins];
    44. double dynzins = 2;
    45. [dynzins_eingabe setFloatValue:dynzins];
    46. double festzins = 10;
    47. [festzins_eingabe setFloatValue:festzins];
    48. }
    49. - (IBAction)showSheet:(id)sender {
    50. [NSApp beginSheet:theSheet
    51. modalForWindow:(NSWindow *)_window
    52. modalDelegate:self
    53. didEndSelector:nil
    54. contextInfo:nil];
    55. }
    56. -(IBAction)closeSheet:(id)sender {
    57. [NSApp endSheet:theSheet];
    58. [theSheet orderOut:sender];
    59. }
    60. -(void)updateProgress:(NSNumber*)Zustand
    61. {
    62. [fortschritt setDoubleValue:(double) anzahl_tage];
    63. }
    64. - (void)rechnen:(id)sender
    65. {
    66. // alles leeren
    67. [zinsAC removeObjects:[zinsAC arrangedObjects]];
    68. NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    69. NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
    70. beginn = [beginn_eingabe dateValue];
    71. NSDate *beginnfest = [beginn_eingabe dateValue];
    72. ende = [ende_eingabe dateValue];
    73. NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    74. [formatter setDateFormat:@"yyyy"];
    75. NSString *beginn_jahr = [formatter stringFromDate: beginn];
    76. [formatter setDateFormat:@"MM"];
    77. NSString *beginn_monat = [formatter stringFromDate: beginn];
    78. [formatter setDateFormat:@"dd"];
    79. NSString *beginn_tag = [formatter stringFromDate: beginn];
    80. [formatter setDateFormat:@"yyyy"];
    81. NSString *ende_jahr = [formatter stringFromDate: ende];
    82. [formatter setDateFormat:@"MM"];
    83. NSString *ende_monat = [formatter stringFromDate: ende];
    84. [formatter setDateFormat:@"dd"];
    85. NSString *ende_tag = [formatter stringFromDate: ende];
    86. NSInteger mode = [[geschaeftsart selectedCell] tag];
    87. switch (mode) {
    88. case 0: zinsauswahl = 5; break;
    89. case 1: zinsauswahl = 8; break;
    90. case 2: zinsauswahl = [dynzins_eingabe floatValue]; break;
    91. case 3: zinsauswahl = [festzins_eingabe floatValue]; break; }
    92. zinsArray=[[NSMutableArray alloc]init];
    93. [fortschritt display];
    94. tageimhalbjahrgesamt = 0;
    95. tagecountergesamt = 0;
    96. tagcounterhalbjahr = 0;
    97. anzahl_tage = 0;
    98. jahrflag = 0;
    99. halbjahrflag = 0;
    100. saldo = 0;
    101. do {
    102. NSTimeInterval secondsElapsed = [ende timeIntervalSinceDate:beginn];
    103. anzahl_tage++;
    104. tagecountergesamt = (secondsElapsed / 86400);
    105. unsigned unitFlags = NSMonthCalendarUnit| NSYearCalendarUnit;
    106. NSDateComponents *comp = [gregorian components:unitFlags fromDate:beginn];
    107. switch ([comp year]) {
    108. case 2002: if ((([comp month] - 1) / 6) + 1 == 1) basiszins = 2.57; else basiszins = 2.47; break;
    109. case 2003: if ((([comp month] - 1) / 6) + 1 == 1) basiszins = 1.97; else basiszins = 1.22; break;
    110. case 2004: if ((([comp month] - 1) / 6) + 1 == 1) basiszins = 1.14; else basiszins = 1.13; break;
    111. case 2005: if ((([comp month] - 1) / 6) + 1 == 1) basiszins = 1.21; else basiszins = 1.17; break;
    112. case 2006: if ((([comp month] - 1) / 6) + 1 == 1) basiszins = 1.37; else basiszins = 1.95; break;
    113. case 2007: if ((([comp month] - 1) / 6) + 1 == 1) basiszins = 2.70; else basiszins = 3.19; break;
    114. case 2008: if ((([comp month] - 1) / 6) + 1 == 1) basiszins = 3.32; else basiszins = 3.19; break;
    115. case 2009: if ((([comp month] - 1) / 6) + 1 == 1) basiszins = 1.62; else basiszins = 0.12; break;
    116. case 2010: if ((([comp month] - 1) / 6) + 1 == 1) basiszins = 0.12; else basiszins = 0.12; break;
    117. case 2011: if ((([comp month] - 1) / 6) + 1 == 1) basiszins = 0.12; else basiszins = 0.37; break;
    118. case 2012: if ((([comp month] - 1) / 6) + 1 == 1) basiszins = 0.12; else basiszins = 0.12; break;
    119. default: basiszins = 0.12; }
    120. jahrflag = [comp year];
    121. halbjahrflag = (([comp month] - 1) / 6) + 1;
    122. tageimhalbjahrgesamt = ++tagcounterhalbjahr;
    123. tageimhalbjahrgesamtstringitem = [NSString stringWithFormat: @"%i", tagcounterhalbjahr];
    124. [formatter setDateFormat:@"dd.MM.yyyy"];
    125. zeitraum = [NSString stringWithFormat:@"%@ - %@", [formatter stringFromDate: beginnfest], [formatter stringFromDate: beginn]];
    126. if (mode == 3) {
    127. zinssatz = zinsauswahl; }
    128. else {
    129. zinssatz = basiszins + zinsauswahl; }
    130. if ( ( ([comp year] % 4 == 0) && ([comp year] % 100 != 0) ) || ([comp year] %400 == 0) ) {
    131. tageszinsen = ((zinssatz / 100) / 366) * [betrag_eingabe floatValue]; }
    132. else {
    133. tageszinsen = ((zinssatz / 100) / 365) * [betrag_eingabe floatValue]; }
    134. zinsen = tageszinsen*tageimhalbjahrgesamt;
    135. zinsenimzeitraumstringitem = [NSString stringWithFormat: @"%.2f Euro", zinsen];
    136. zinssatzstringitem = [NSString stringWithFormat: @"%.2f %%", zinssatz];
    137. tageszinsenstringitem = [NSString stringWithFormat: @"%.4f Euro", tageszinsen];
    138. saldo += tageszinsen;
    139. saldostringitem = [NSString stringWithFormat: @"%.2f Euro", saldo];
    140. [formatter setDateFormat:@"ddMM"];
    141. NSString *beginnabfrage = [formatter stringFromDate: beginn];
    142. [dateComponents setDay:+1];
    143. beginn = [gregorian dateByAddingComponents:dateComponents toDate: beginn options:0];
    144. if (![[betrag_eingabe stringValue] isEqualToString: @""]) {
    145. if ( [beginnabfrage isEqualToString: @"3006"] || [beginnabfrage isEqualToString: @"3112"]) {
    146. // Array schreiben
    147. newDic=[NSMutableDictionary dictionaryWithObjectsAndKeys:
    148. zeitraum, @"zeitraum",
    149. tageimhalbjahrgesamtstringitem, @"tage",
    150. zinssatzstringitem, @"zinssatz",
    151. tageszinsenstringitem, @"protag",
    152. zinsenimzeitraumstringitem, @"imzeitraum",
    153. saldostringitem, @"saldo",
    154. nil];
    155. [zinsAC addObject:newDic];
    156. tageimhalbjahrgesamt = 0;
    157. tagcounterhalbjahr = 0;
    158. beginnfest = beginn;
    159. }
    160. }
    161. [self performSelectorOnMainThread:@selector(updateProgress:) withObject:[NSNumber numberWithInt:anzahl_tage] waitUntilDone:NO];
    162. } while (tagecountergesamt >0);
    163. if (![[betrag_eingabe stringValue] isEqualToString: @""]) {
    164. // Array schreiben
    165. newDic=[NSMutableDictionary dictionaryWithObjectsAndKeys:
    166. zeitraum, @"zeitraum",
    167. tageimhalbjahrgesamtstringitem, @"tage",
    168. zinssatzstringitem, @"zinssatz",
    169. tageszinsenstringitem, @"protag",
    170. zinsenimzeitraumstringitem, @"imzeitraum",
    171. saldostringitem, @"saldo",
    172. nil];
    173. [zinsAC addObject:newDic];
    174. gesamtstand = saldo + [betrag_eingabe floatValue];
    175. }
    176. // Ausgabe
    177. NSString *betragString = [NSString stringWithFormat:@"%.2f Euro",[betrag_eingabe floatValue]];
    178. [betrag_ausgabe setStringValue:betragString];
    179. if (![[betrag_eingabe stringValue] isEqualToString: @""]) {
    180. gesamtstring = [NSString stringWithFormat:@"%.2f Euro",gesamtstand];
    181. } else {
    182. gesamtstring = @"Sie müssen einen Rechnungsbetrag eingeben!";
    183. }
    184. [gesamt_ausgabe setStringValue:gesamtstring];
    185. NSString *zinssatzString = [NSString stringWithFormat:@"%.2f %%",zinssatz];
    186. [zinssatz_ausgabe setStringValue:zinssatzString];
    187. NSString *tageString = [NSString stringWithFormat:@"%i Tage",anzahl_tage];
    188. [tage_ausgabe setStringValue:tageString];
    189. NSString *tageszinsenString = [NSString stringWithFormat:@"%.2f Euro",tageszinsen];
    190. [protag_ausgabe setStringValue:tageszinsenString];
    191. NSString *beginnString = [NSString stringWithFormat:@"%@.%@.%@",beginn_tag, beginn_monat, beginn_jahr];
    192. [beginn_ausgabe setStringValue:beginnString];
    193. NSString *endeString = [NSString stringWithFormat:@"%@.%@.%@",ende_tag, ende_monat, ende_jahr];
    194. [ende_ausgabe setStringValue:endeString];
    195. [self performSelectorOnMainThread:@selector(calcCompleted) withObject:nil waitUntilDone:NO];
    196. }
    197. - (IBAction)mainthread:(id)sender
    198. {
    199. piinterval = [ende timeIntervalSinceDate:beginn] / 86400;
    200. [fortschritt setMaxValue:piinterval];
    201. [fortschritt setMinValue:0.0];
    202. [fortschritt setHidden:FALSE];
    203. [fortschritt startAnimation: self];
    204. [self performSelectorInBackground:@selector(rechnen:) withObject:nil];
    205. }
    206. - (void)calcCompleted
    207. {
    208. [fortschritt stopAnimation: self];
    209. [fortschritt setHidden:TRUE];
    210. }
    211. @end
    Alles anzeigen
    Ich bin gegen Signaturen!!!
  • MCDan schrieb:

    Was funktioniert denn nicht?

    In updateProgress: solltest Du den ProgressIndicator natürlich mit dem übergeben Wert aktualisieren und nicht mit anzahl_tage. ;)


    Das wars ;) Jetzt funzt es! Danke!

    Ich krieg aber immer noch dieses CoreAnimation: warning, deleted thread with uncommitted CATransaction; set CA_DEBUG_TRANSACTIONS=1 in environment to log backtraces. auf der Konsole.

    Das CA_DEBUG_TRANSACTIONS=1 spuckt mir folgendes aus:

    CoreAnimation: warning, deleted thread with uncommitted CATransaction; created by:
    0 QuartzCore 0x00007fff8610bb95 _ZN2CA11Transaction4pushEv + 219
    1 QuartzCore 0x00007fff8610b76d _ZN2CA11Transaction15ensure_implicitEv + 273
    2 QuartzCore 0x00007fff861128ab _ZN2CA5Layer13thread_flags_EPNS_11TransactionE + 37
    3 QuartzCore 0x00007fff861127e3 _ZN2CA5Layer4markEPNS_11TransactionEjj + 79
    4 QuartzCore 0x00007fff8611274a _ZN2CA5Layer25set_needs_display_in_rectERK6CGRect + 392
    5 AppKit 0x00007fff8b89047d -[_NSViewBackingLayer setNeedsDisplayInRect:] + 206
    6 QuartzCore 0x00007fff8611259f -[CALayer setNeedsDisplay] + 62
    7 AppKit 0x00007fff8b8903a9 -[_NSViewBackingLayer setNeedsDisplay] + 73
    8 AppKit 0x00007fff8b891bc8 -[NSView(NSInternal) _setLayerNeedsDisplayInViewRect:] + 566
    9 AppKit 0x00007fff8b6cdaad -[NSView setNeedsDisplayInRect:] + 809
    10 AppKit 0x00007fff8b8d51d5 -[NSTableBinder _updateContent] + 392
    11 AppKit 0x00007fff8b8d4f67 -[NSTableBinder _observeValueForKeyPath:ofObject:context:] + 84
    12 AppKit 0x00007fff8b8d4d78 -[NSTableBinder observeValueForKeyPath:ofObject:change:context:] + 63
    13 Foundation 0x00007fff8659f860 NSKeyValueNotifyObserver + 390
    14 Foundation 0x00007fff86599d23 -[NSObject(NSKeyValueObservingPrivate) _notifyObserversForKeyPath:change:] + 967
    15 AppKit 0x00007fff8b830ed3 -[NSController _notifyObserversForKeyPath:change:] + 209
    Ich bin gegen Signaturen!!!