Slideshow
-
Danke für den Tip mit der Liste, werde ich gleich mal versuchen.
Dass es sowas wie den FileSystemWatcher gibt ist ja cool. Das eröffnet ganz neue Perspektiven.
Mein momentaner Ansatz war, das Array einmal durchlaufen zu lassen, den Ordner dann neu zu scannen und das Array erneut zu füllen. Dann wären die Bilder halt im nächsten Durchlauf mit dabei.Deine Idee ist aber natürlich klasse! Wobei, der FileSystemWatcher verbraucht ja noch mal Resoourcen und wie Du dem Startpost entnehmen kannst, hab ich da momentan noch ein kleines Problem
-
this->aBilder = System::IO::Directory::GetFiles(this->folderBrowserDialog1->SelectedPath, L"*.jpg"); int x = this->aBilder->Length; for (int i = 0; i < x; i++) { this->panel1->BackgroundImage = System::Drawing::Image::FromFile(this->aBilder[i]); this->panel1->Refresh(); }
wie ich das sehe müsste eigentlich die schleife blitzschnell durchsein, da du ja nix dazwichen machst!
panel.BackgroundImage.Dispose(); //Freigabe
Wenn du noch ein bisschen mehr code zeigst kann ich dir mehr helfen
am besten bau einen Timer ein um bei deiner Slideshow die Bildzeit(wie lange es angezeigt werden soll bis zum nächsten).
Mehr Code = Mehr Hilfe
mfg Helfender Helfer
-
Mit kleinen Bildern lief das auch so zügig durch, dass man den Bildwechsel erst beim letzten Bild gesehen hat (ein Versuch mit einem billigen time-Abgleich hat dann schon mehr Erkenntnis geliefert).
Wenn ich aber einen Ordner mit 10MP-Bilddateien auswähle, braucht er schon ziemlich lange für den Bildwechsel und nach 21Bildern ist mein Speicher voll und meine Kiste gibt gequält auf. (Ich vermute irgendwie, dass Jpegs intern in BMPs umgewandelt werden. Anders kann ich mir diesen riesigen Speicherhunger nicht erklären)Ich sitze jetzt gerade an dem neuen Timer, der bei Ablauf des Intervalls einen Bildwechsel auslösen soll (Sowas dauert bei mir immer etwas
Hab halt nur damals in dem einen Semester mit c++ gearbeitet)
Deshalb kann ich dir momentan nicht mehr Code liefern.Zu Testzwecken lasse ich bei Aktivierung der Slideshow ein Bild anzeigen und beobachte das Ganze im Taskmanager.
Je öfter ich starte, desto mehr Speicher verschlingt das Ding.this->panel1->BackgroundImage->Dispose();
Hab ich ja auch vermutet (war das einzige in der automatisch vorgeschlagenen Liste, dass mir vernünfitg erschien.
Allerdings meckert er: 'Dispose': Ist kein Element von 'System::Drawing::Image'
Ich solle gefälligst den Destruktor verwenden.
-
Ja, intern werden Bilder als Bitmap verwaltet (daher der größere Speicherverbrauch als auf Festplatte).
Aber du brauchst ja gar nicht alle Bilder sofort zu laden. Es reicht doch, wenn du eine Liste von Dateinamen hast und nur jeweils das aktuelle Bild einlädst (evtl. noch eine Prefetch-Funktion, so daß schon das nächste Bild eingeladen wird, während das aktuelle angezeigt wird).
Und nach dem Anzeigen dann wieder das Bild aus dem Speicher löschen.
-
Genau das ist ja der Plan.
Habe ja auch nur ein Array (bald die Liste) mit den Dateinamen.In einer Schleife wurde dann per
this->panel1->BackgroundImage = System::Drawing::Image::FromFile(this->aFileNames[0]); this->panel1->Refresh();
das neue Bild ins Panel geladen.
Beim Laden des zweiten Bildes, werden aber die bereits verwendeten Ressourcen nicht freigegeben. Darum steigt der Speicherbedarf konstant mit den angezeigten Bildern an.
-
Hier meine Methode, die beim Starten aufgerufen wird:
private: System::Void startenToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) { if ( this->folderBrowserDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK ) { if ( this->folderBrowserDialog1->SelectedPath != nullptr ) { this->aFileNames = System::IO::Directory::GetFiles(this->folderBrowserDialog1->SelectedPath, L"*.jpg"); this->lFileNames->Clear(); for (int i = 0; i < this->aFileNames->Length; i++) this->lFileNames->Add(this->aFileNames[i]); if (this->lFileNames->Count > 0) { this->tTimer->Interval = (double)(this->iInterval * 1000); this->tTimer->Enabled = true; this->panel1->BackgroundImage = System::Drawing::Image::FromFile(this->lFileNames[0]); this->panel1->Refresh(); } } } }
Ich muss leider über das Array gehen.
this->aFileNames = System::IO::Directory::GetFiles(this->folderBrowserDialog1->SelectedPath, L"*.jpg");
Wie bekomme ich die Dateinamen ohne Umweg in die Liste?
Hier der Code der durch das Timerevent aufgerufen wird:
private: System::Void TimerEventProcessor(System::Object^ sender, System::Timers::ElapsedEventArgs^ e) { this->lFileNames->Remove(lFileNames[0]); if (this->lFileNames->Count > 0) { this->tTimer->Stop(); this->panel1->BackgroundImage = System::Drawing::Image::FromFile(this->lFileNames[0]); this->tTimer->Start(); this->panel1->Refresh(); } else { this->tTimer->Stop(); } }
Und auch hierzu habe ich eine Frage. Code, der in der Schleife nach
this->panel1->Refresh();
steht (hier nicht der Fall) wird nicht berücksichtigt. Darum muss ich den Timer-Start davor ausführen. Ich verstehe nur nicht, warum das so ist
-
Ich habe das jetzt mal genau im TaskManager beobachtet.
Nach 14 Bilder verbrauchte mein Programm ca. 370MB, beim 15 Bild stieg es kurz auf 400MB und fiel dann plötzlich auf 40MB.
Danach stieg es wieder an.Jetzt ist die Slideshow beendet, und es werden trotzdem noch 137MB verbraucht; sehr merkwürdig.
Es wird zwischendurch also anscheinend mal Speicher freigemacht, das scheint aber nur sporadisch zu geschehen. Bei großen Dateien und kurzer Anzeigedauer könnte das schnell ein Problem werden.
Außerdem finde ich den Speicherbedarf für diese Art Programm auch ziemlich unverschämtKlicke ich nun, nach dem Ende der Slideshow, auf einen Menüeintrag, fällt der benötigte Speicher auf 17MB..
-
Etwas verwirrend geschrieben dein Programmschen
ich hab mir mal die zeit genommen und dir was zusammen gebastelt.
Designer Code:
namespace SlideShow { partial class Form1 { /// <summary> /// Erforderliche Designervariable. /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// Verwendete Ressourcen bereinigen. /// </summary> /// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Vom Windows Form-Designer generierter Code /// <summary> /// Erforderliche Methode für die Designerunterstützung. /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. /// </summary> private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.timer1 = new System.Windows.Forms.Timer(this.components); this.fileSystemWatcher1 = new System.IO.FileSystemWatcher(); this.pictureBox1 = new System.Windows.Forms.PictureBox(); ((System.ComponentModel.ISupportInitialize)(this.fileSystemWatcher1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.SuspendLayout(); // // timer1 // this.timer1.Interval = 200; this.timer1.Tick += new System.EventHandler(this.timer1_Tick); // // fileSystemWatcher1 // this.fileSystemWatcher1.EnableRaisingEvents = true; this.fileSystemWatcher1.SynchronizingObject = this; this.fileSystemWatcher1.Created += new System.IO.FileSystemEventHandler(this.fileSystemWatcher1_Created); this.fileSystemWatcher1.Deleted += new System.IO.FileSystemEventHandler(this.fileSystemWatcher1_Deleted); // // pictureBox1 // this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill; this.pictureBox1.Location = new System.Drawing.Point(0, 0); this.pictureBox1.Name = "pictureBox1"; this.pictureBox1.Size = new System.Drawing.Size(527, 429); this.pictureBox1.TabIndex = 0; this.pictureBox1.TabStop = false; // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(527, 429); this.Controls.Add(this.pictureBox1); this.Name = "Form1"; this.Text = "Form1"; this.Load += new System.EventHandler(this.Form1_Load); ((System.ComponentModel.ISupportInitialize)(this.fileSystemWatcher1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); this.ResumeLayout(false); } #endregion private System.Windows.Forms.Timer timer1; private System.IO.FileSystemWatcher fileSystemWatcher1; private System.Windows.Forms.PictureBox pictureBox1; } }
Hier der Rest:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace SlideShow { public partial class Form1 : Form { //Bilder namen private List<String> fileNames; //aktuelle position in der Liste private int position; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { //Hier die Position auf 0 setzen (am Anfang) position = 0; //Hier die Bilder überwachen fileSystemWatcher1.Path = "C:\\Dokumente und Einstellungen\\All Users\\Dokumente\\Eigene Bilder\\Beispielbilder"; fileNames = new List<String>(System.IO.Directory.GetFiles("C:\\Dokumente und Einstellungen\\All Users\\Dokumente\\Eigene Bilder\\Beispielbilder", "*.jpg*")); timer1.Start(); } private void timer1_Tick(object sender, EventArgs e) { if (position == fileNames.Count) position = 0; if (pictureBox1.Image != null) pictureBox1.Image.Dispose(); pictureBox1.Image = Image.FromFile(fileNames[position]); position++; } private void fileSystemWatcher1_Created(object sender, System.IO.FileSystemEventArgs e) { fileChange(); } private void fileSystemWatcher1_Deleted(object sender, System.IO.FileSystemEventArgs e) { fileChange(); } private void fileChange() { position = 0; fileNames.Clear(); fileNames = new List<String>(System.IO.Directory.GetFiles("C:\\Dokumente und Einstellungen\\All Users\\Dokumente\\Eigene Bilder\\Beispielbilder", "*.jpg*")); fileSystemWatcher1.Path = "C:\\Dokumente und Einstellungen\\All Users\\Dokumente\\Eigene Bilder\\Beispielbilder"; } } }
Außerdem hast du im falschen Forumteil gepostet das was du Programmierst ist C++/CLI
Wenn du was nicht verstehst sag bescheid
mfg Helfender Helfer
-
Helfender Helfer schrieb:
Etwas verwirrend geschrieben dein Programmschen
ich hab mir mal die zeit genommen und dir was zusammen gebastelt.
Alles Übungssache; ich blicke da prima durch
Ist aber wirklich super nett von Dir, vielen Dank.So, zu Deinem Code. Ich habe ein bisschen gebraucht, verstehe aber alles (denke ich).
Ich probiere mal, das in mein bestehendes Programm zu integrieren; ist mir lieber, als deinen Code komplett zu übernehmen.Wenn sich der Ordnerinhalt nun ändert, wird aber die Wiedergabeliste gelöscht. D.h. die Slideshow würde wieder mit dem erten Bild beginnen; sehe ich das richtig? An dem Punkt würde ich dann wohl was ändern.
Du hast Dispose() nur überschrieben, um es mir zu zeigen, oder? Zumindest kann ich keine Verwendung im Code entdecken.
Ach so, eins noch. Du verwendest eine PictureBox statt meinem Panel. Gibt es dafür einen Grund? Performance?
Nochmals vielen Dank.
Will Dir aber nicht zuviel Arbeit machen (auch wenn Du das wohl ziemlich schnell geproggt bekommst).
-
PictureBox bringt viel mehr da er extra für solche dinge gemacht wurde und kein Panel
und Performence bringt es auch wie ich im taskmanager sehe bleibt er nur bei 12.000.
if (pictureBox1.Image != null) pictureBox1.Image.Dispose();
da hab ich es benutzt! Ganz am anfang ist doch das Image null und das muss man abfangen sonst gibts Fehler, da man keine Ressourcen freigeben kann wo nix ist.
Das der wieder von vorne beginnt ist besser, denn falls das nächste bild was du anzeigen willst wiedergeben willst weiß er nicht wo was ist. du könntest das natürlich auch umändern aber ich wollt dir nur ein bisschen code zeigen ^^ damit du ein bisschen die Richtung hast.
Und das du dein code benutzt ist auch gut denn wenn man es von anderen nur ganze zeit vorgaugelt bekommt lernt man selbst auch nix dabei.
mfg Helfender Helfer
-
this->lFileNames = gcnew System::Collections::Generic::List<System::String>(System::IO::Directory::GetFiles(this->folderBrowserDialog1->SelectedPath, L"*.jpg"));
bzw.
this->lFileNames = gcnew System::Collections::Generic::List<System::String^>(System::IO::Directory::GetFiles(this->folderBrowserDialog1->SelectedPath, L"*.jpg"));
Das funktioniert bei mir leider nicht so einfach. Eine Konvertierung von array in List bzw. String ist so nicht möglich.
Leider bekomme ich auch das Überschreiben von Dispose() nicht hin, weil bei mir die Syntax wohl komplett anders lauten muss.
Jetzt bekomme ich wohl doch zu spüren, dass ich im falschen Subforum bin *lach*
-
Dann mach ne schleife die das dann macht also:
List<String> list = new List<string>(); String[] allfiles = System.IO.Directory.GetFiles(this.folderBrowserDialog1.SelectedPath, "*.jpg"); foreach(String file in allfiles) { list.Add(file); }
und dein Dispose aufruf? Zeigmal wie du ihn aufrufst. Benutzt du ne PictureBox?
-
Mit der Schleife hab ich es auch gelöst. Ist halt nur immer unschön, unnötig zusätzliche Variablen zu brauchen; aber so groß ist das Array ja nicht.
So sieht's aus:
private: System::Void TimerEventProcessor(System::Object^ sender, System::Timers::ElapsedEventArgs^ e) { if (this->lFileNames->Count > 0) { this->tTimer->Stop(); this->pictureBox1->Image = System::Drawing::Image::FromFile(this->lFileNames[0]); this->lFileNames->Remove(lFileNames[0]); this->tTimer->Start(); } else { das Gleiche, nur wird halt zusätzlich die Liste neu gefüllt } }
So hab ich es versucht:
private: System::Void TimerEventProcessor(System::Object^ sender, System::Timers::ElapsedEventArgs^ e) { if (this->lFileNames->Count > 0) { this->tTimer->Stop(); this->pictureBox1->Dispose(); this->pictureBox1->Image = System::Drawing::Image::FromFile(this->lFileNames[0]); this->lFileNames->Remove(lFileNames[0]); this->tTimer->Start(); } else { das Gleiche, nur wird halt zusätzlich die Liste neu gefüllt } }
c:\users\marian\documents\visual studio 2005\projects\fotoslider\fotoslider\Form1.h(288) : error C2039: 'Dispose': Ist kein Element von 'System::Windows::Forms::PictureBox' c:\windows\microsoft.net\framework\v2.0.50727\system.windows.forms.dll: Siehe Deklaration von 'System::Windows::Forms::PictureBox' Sie sollten stattdessen den Destruktor, "~PictureBox", aufrufen.
Verstehe das nicht, weil er mir Dispose() ja sogar vorschlägt, wenn ich this->pictureBox1-> schreibe.
Bewundernswert, dass es bei Dir nur 12MB braucht
Ich mache gerade mal einen Testlauf (1000 Bilder, solange bis er abstürzt).
Der Bedarf steigt auf so ca. 250MB. Dann werden Ressourcen freigegeben und er fällt auf 40. Zwar gierig, aber es läuft konstant.
Mit was für Bildern hast Du es denn getestet? Die 10MegaPixel-Bilder aus meiner Spiegelreflex muss es schon verkraften
-
pictureBox1->Image->Dispose()
Helfender Helfer
-
Ach du solltest natürlich auch noch beachten das das Image am anfang null ist! also:
private: System::Void TimerEventProcessor(System::Object^ sender, System::Timers::ElapsedEventArgs^ e) { if (this->lFileNames->Count > 0) { this->tTimer->Stop(); if(this->pictureBox1->Image != null) this->pictureBox1->Image->Dispose(); this->pictureBox1->Image = System::Drawing::Image::FromFile(this->lFileNames[0]); this->lFileNames->Remove(lFileNames[0]); this->tTimer->Start(); } else { das Gleiche, nur wird halt zusätzlich die Liste neu gefüllt }
}
-
Ah sorry, hatte es jetzt nur mal auf die Schnelle hineingeschrieben, um die Fehlermeldung posten zu können.
Bei meinen ersten Versuchen hab' ich das "Image" mit drin gehabt
c:\users\marian\documents\visual studio 2005\projects\fotoslider\fotoslider\Form1.h(288) : error C2039: 'Dispose': Ist kein Element von 'System::Drawing::Image' c:\windows\microsoft.net\framework\v2.0.50727\system.drawing.dll: Siehe Deklaration von 'System::Drawing::Image' Sie sollten stattdessen den Destruktor, "~Image", aufrufen.
-
achso C++
mach ein
delete(this->pictureBox1->Image);
anstatt das
this->pictureBox1->Image->Dispose();
-
AHHHHHHHHHHHHHHH, ich halts nicht aus. Ich Volldepp
War so verzweifelt, dass ich Sachen wie ~this->pictureBox1->Image etc. versucht habe.
delete() ... es darf nicht wahr sein. Dabei hab' ich das sogar mal in einem Projekt zum Beenden desselbigen verwendet, bis ich dann irgendwann entdeckt habe, dass man das Formular über Close() beenden kann.Danke, jetzt verbraucht mein Programm ziemlich konstant 30MB bei 10MP-Dateien; das wird wohl die ungefähre Größe des entsprechenden Bitmaps sein.
Du hast meine Party echt gerettet, tausend Dank
-
Und da bin ich leider auch schon wieder
Nachdem nun alles inkl. Vollbildmodus und genügsamen Speichermanagement funktioniert und sich mein Projekt auf einem anderen Rechner auch zu einer lauffähigen Exe kompilieren ließ, habe ich nun ein dickes Problem hier.
Bewegt man in dem Moment des Bildwechsels ein anderes Fenster über die Picturebox, so stürzt mein Programm mit folgender Meldung ab:
************** Ausnahmetext **************
System.ArgumentException: Ungültiger Parameter.
bei System.Drawing.Image.get_Width()
bei System.Drawing.Image.get_Size()
bei System.Windows.Forms.PictureBox.ImageRectangleFromSizeMode(PictureBoxSizeMo
de mode)
bei System.Windows.Forms.PictureBox.get_ImageRectangle()
bei System.Windows.Forms.PictureBox.OnPaint(PaintEventArgs pe)
bei System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
bei System.Windows.Forms.Control.WmPaint(Message& m)
bei System.Windows.Forms.Control.WndProc(Message& m)
bei System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
bei System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
bei System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)Mein Bild wird in der Mainform in einer Picturebox angezeigt. Ausgelöst wird der Bildwechsel durch ein Timerevent:
private: System::Void TimerEventProcessor(System::Object^ sender, System::Timers::ElapsedEventArgs^ e) { //Speicher vom alten Bild befreien delete(this->pictureBox1->Image); //Wenn die Dateinamenliste noch Einträge enthält... if (this->lFileNames->Count > 0) { this->tTimer->Stop(); //bRandom enthält den Bool für die zufällige Bildwiedergabe if (bRandom == true) { int x = this->lFileNames->Count; int y = this->random->Next(0, x); //Überprüfung, für den Fall, dass inzwischen ein Bild gelöscht wurde while ((this->lFileNames->Count > 0) && (System::IO::File::Exists(this->lFileNames[y]) == false)) { this->lFileNames->Remove(lFileNames[y]); x = this->lFileNames->Count; y = this->random->Next(0, x); } //Wenn die Dateinamenliste danach immer noch Einträge enthält... if (this->lFileNames->Count > 0) { this->pictureBox1->Image = System::Drawing::Image::FromFile(this->lFileNames[y]); this->lFileNames->Remove(lFileNames[y]); } } else { //Überprüfung, für den Fall, dass inzwischen ein Bild gelöscht wurde while ((this->lFileNames->Count > 0) && (System::IO::File::Exists(this->lFileNames[0]) == false)) { this->lFileNames->Remove(lFileNames[0]); } //Wenn die Dateinamenliste danach immer noch Einträge enthält... if (this->lFileNames->Count > 0) { this->pictureBox1->Image = System::Drawing::Image::FromFile(this->lFileNames[0]); this->lFileNames->Remove(lFileNames[0]); } } this->tTimer->Start(); } else { this->tTimer->Stop(); loadFileNames(); if (bRandom == true) { int x = this->lFileNames->Count; int y = this->random->Next(0, x); this->pictureBox1->Image = System::Drawing::Image::FromFile(this->lFileNames[y]); this->lFileNames->Remove(lFileNames[y]); } else { this->pictureBox1->Image = System::Drawing::Image::FromFile(this->lFileNames[0]); this->lFileNames->Remove(lFileNames[0]); } this->tTimer->Start(); } }
Der Absturz erfolgt auch, wenn man die Fenstergröße während des Bildwechsel verändert.
Ich würde es mit einer try-Anweisung versuchen, hab aber keine Ahnung, wie das entsprechende catch lauten muss
-
Ich habe es in meiner showPicture-Methode einfach mal so probiert:
private: System::Void showPicture(int x) { try { this->pictureBox1->Image = System::Drawing::Image::FromFile(this->lFileNames[x]); this->lFileNames->Remove(lFileNames[x]); } catch(...) {} }
Der Absturz erfolgt aber nach wie vor. Wenn ich mir die Fehlermeldung so anschaue, könnte es auch an der Picturebox ansich, bzw dem SizeMode liegen.
this->pictureBox1->SizeMode = System::Windows::Forms::PictureBoxSizeMode::Zoom;
Hat jemand eine Idee?