Короче говоря, я очень далек от опытного программиста, на самом деле мои самые сложные программы были либо простыми манипуляциями с ASCII-строками, простыми математическими вычислениями и поиском / сортировкой массивов в Free Pascal или более поздних версиях, Delphi 7 и Джава. Это было несколько лет назад, когда я изучал программирование на факультете средней школы (это был простой Паскаль). Позже я стал программистом (познакомился с D7 и Java, и немного C ++), но я бросил учебу по программированию по личным причинам, и с тех пор я не написал ни одной строчки кода.
Эмм, извините за длинное вступление, так что ... Недавно я решил возродить программирование в качестве своего хобби, главным образом потому, что не нашел подходящей программы для некоторых задач, которые я хотел бы выполнять долгое время. Несмотря на мое слабое понимание таких довольно простых вещей, как явные параметры, указатели, объекты, классы, конструкторы и потоки, с помощью книги по программированию, файлов справки Delphi и Интернета, мне удалось написать простую программу на Delphi 7. который может загружать и отображать определенные форматы файлов изображений в заданном каталоге с использованием внешних библиотек, делает возможным произвольное переключение между ними (с помощью графического интерфейса пользователя) и записывать некоторую информацию (главным образом для целей отладки) в текстовые файлы.
Тем не менее, я столкнулся с проблемой в текущей версии кода, когда я попытался сделать функцию загрузки и отображения изображения многопоточным. Во-первых, для лучшего понимания я объясню логику моей программы.
Прежде всего, в событии FormCreate главной формы программа ищет поддерживаемые файлы изображений в текущем каталоге (где находится exe). Если файлов изображений нет, текущий каталог устанавливается на верхний уровень (со стандартным символом файловой системы Windows "..") и проверяется на наличие изображений. Поддерживаемые файлы изображений определяются расширениями файлов. В любом случае, эта функция проводника сохраняет имя найденного изображения и идентификатор типа файла (который является байтом) в динамическом массиве. Затем, используя этот массив в качестве ссылки, первый поддерживаемый файл изображения загружается правильной библиотекой и отображается в форме. В графическом интерфейсе есть кнопки и поле со списком для переключения между изображениями, с переменными установки событий каждого элемента управления OnClick или OnSelect (combobox) относительно предположительно текущего файла изображения и вызова функции загрузки и отображения изображений, которая использует массив ссылок.
Проблема в том, что некоторые изображения настолько велики, что загрузка занимает заметное время, поэтому графический интерфейс не может отвечать, пока изображение не будет полностью загружено и отображено. Я попытался сделать эту программу поточной, инициализируя каждую функцию загрузчика изображений как поток. Хотя графический интерфейс стал более отзывчивым, в программе, безусловно, есть две новые ошибки.
Во-первых, программа иногда случайным образом аварийно завершает работу при смене изображений, при этом появляются сообщения, ссылающиеся либо на « Ошибка JPEG # 58 » (предположительно, означая «недопустимая структура файла» во встроенной библиотеке Delphi jpeg) , « EAccessViolation» исключение , « EOSError» исключение (включая « Системная ошибка, код 5 »), « неизвестное программное исключение » , " Ошибка выполнения 216 " и сообщения об ошибках в ячейках памяти и неудачных операциях чтения. До использования потоков ни одно из этих сообщений об ошибках не появлялось, но я, безусловно, хочу (и должен) использовать потоки в программе.
Другая, незначительная ошибка заключается в том, что при быстром последовательном нажатии на кнопки интерфейса происходит загрузка и отображение всех данных, хотя и медленным, а затем быстрым способом. У меня действительно нет идеи о том, как «убить» поток и запустить его «снова», чтобы загрузить текущий текущий файл вместо устаревшего, который он пытался загрузить несколько сотен миллисекунд назад.
Я запускаю тему следующим образом:
LoaderThread := CreateThread(nil, 0, Addr(LoadPicture), nil, 0, LoaderThreadID);
CloseHandle(LoaderThread);
Я использую это два раза в событии FormCreate главной формы (хотя только один из них выполняется при любом запуске) и в событиях OnClick или OnSelect элементов управления GUI для облегчения выполнения желаемой функции элемента управления (например, переход кпоследнее изображение).
Есть предложения?Заранее спасибо!:)
ОБНОВЛЕНИЕ: Вот некоторые (ну, почти все) мой исходный код:
procedure TMainForm.FormCreate(Sender: TObject);
begin
MainForm.DoubleBuffered := true;
MainJPEG := TJPEGImage.Create;
MainJPEG.ProgressiveDisplay := true;
MainJPEG.Smoothing := true;
MainJPEG.Performance := jpBestQuality;
MainPNG := TPNGObject.Create;
MainGIF := TGIFImage.Create;
AssignFile(Log, '_NyanLog.txt');
CurrentDir := GetCurrentDir;
ExploreCurrentDir;
if CurrentDirHasImages = false then
begin
SetCurrentDir('..');
CurrentDir := GetCurrentDir;
ExploreCurrentDir;
end;
if CurrentDirHasImages = true then
begin
CurrentFilename := ImagesOfCurrentDir[CurrentPos].Filename;
CurrentFiletype := ImagesOfCurrentDir[CurrentPos].Filetype;
LoaderThread := BeginThread(nil, 0, Addr(LoadImage), nil, 0, LoaderThreadID);
CloseHandle(LoaderThread);
if Length(ImagesOfCurrentDir) > 1 then
begin
MainForm.NextButton.Enabled := true;
MainForm.EndButton.Enabled := true;
MainForm.SlideshowButton.Enabled := true;
MainForm.SlideshowIntervalUpDown.Enabled := true;
end;
UpdateTitleBar;
end
else UpdateTitleBar;
end;
procedure ExploreCurrentDir;
var
Over: boolean;
begin
CurrentPos := 0;
Over := false;
ReWrite(Log);
Write(Log, 'blablabla');
if FindFirst(CurrentDir+'\*.*', faAnyFile-faDirectory, Find) = 0 then
begin
CurrentFilename := Find.Name;
DetermineFiletype;
if CurrentFiletype <> UNSUPPORTED then
begin
SetLength(ImagesOfCurrentDir, CurrentPos+1);
ImagesOfCurrentDir[CurrentPos].Filename := CurrentFilename;
ImagesOfCurrentDir[CurrentPos].Filetype := CurrentFiletype;
MainForm.ImagelistComboBox.AddItem(CurrentFilename, nil);
Write(Log, 'blablabla');
CurrentPos := Succ(CurrentPos);
end;
while Over = false do
begin
if FindNext(Find) = 0 then
begin
CurrentFilename := Find.Name;
DetermineFiletype;
if CurrentFiletype <> UNSUPPORTED then
begin
SetLength(ImagesOfCurrentDir, CurrentPos+1);
ImagesOfCurrentDir[CurrentPos].Filename := CurrentFilename;
ImagesOfCurrentDir[CurrentPos].Filetype := CurrentFiletype;
MainForm.ImagelistComboBox.AddItem(CurrentFilename, nil);
Write(Log, 'blablabla');
CurrentPos := Succ(CurrentPos);
end;
end
else
begin
FindClose(Find);
Over := true;
end;
end;
CurrentDirImageCount := Length(ImagesOfCurrentDir);
CurrentDirHasImages := true;
Write(Log, 'blablabla');
end;
if CurrentDirHasImages = false then Write(Log, 'blablabla');
CloseFile(Log);
CurrentPos := 0;
end;
procedure LoadImage; //procedure #1 which should be used in a thread
begin
if CurrentFiletype = BMP then
begin
MainForm.MainImage.Picture := nil;
MainForm.MainImage.Picture.LoadFromFile(CurrentFilename)
end
else
if CurrentFiletype = JPEG then
begin
MainForm.MainImage.Picture := nil;
MainJPEG.LoadFromFile(CurrentFilename);
MainForm.MainImage.Picture.Assign(MainJPEG);
end
else
if CurrentFiletype = PNG then
begin
MainForm.MainImage.Picture := nil;
MainPNG.LoadFromFile(CurrentFilename);
MainForm.MainImage.Picture.Assign(MainPNG);
end
else
if CurrentFiletype = GIF then
begin
MainForm.MainImage.Picture := nil;
MainGIF.LoadFromFile(CurrentFilename);
MainForm.MainImage.Picture.Assign(MainGIF);
end;
end;
procedure NextImage; //the "NextButton" button from the GUI calls this
begin
if CurrentPos < Length(ImagesOfCurrentDir)-1 then
begin
CurrentPos := Succ(CurrentPos);
CurrentFilename := ImagesOfCurrentDir[CurrentPos].Filename;
CurrentFiletype := ImagesOfCurrentDir[CurrentPos].Filetype;
UpdateTitleBar;
LoaderThread := BeginThread(nil, 0, Addr(LoadImage), nil, 0, LoaderThreadID);
CloseHandle(LoaderThread);
while MainImageIsEmpty = true do
begin
if CurrentPos < Length(ImagesOfCurrentDir)-1 then
begin
CurrentPos := Succ(CurrentPos);
CurrentFilename := ImagesOfCurrentDir[CurrentPos].Filename;
CurrentFiletype := ImagesOfCurrentDir[CurrentPos].Filetype;
UpdateTitleBar;
LoaderThread := BeginThread(nil, 0, Addr(LoadImage), nil, 0, LoaderThreadID);
CloseHandle(LoaderThread);
end;
if CurrentPos = CurrentDirImageCount-1 then Break;
end;
end;
if CurrentPos = CurrentDirImageCount-1 then
begin
MainForm.NextButton.Enabled := false;
MainForm.EndButton.Enabled := false;
MainForm.SlideshowButton.Enabled := false;
MainForm.SlideshowIntervalUpDown.Enabled := false;
end;
MainForm.PrevButton.Enabled := true;
MainForm.StartButton.Enabled := true;
end;
procedure PrevImage; //called by "PrevButton"
begin
//some code, calls LoadImage
//almost the same logic as above for a backward step among the images
end;
procedure FirstImage; //called by "StartButton"
begin
//some code, calls LoadImage
end;
procedure LastImage; //called by "EndButton"
begin
//some code, calls LoadImage
end;
procedure Slideshow; //procedure #2 which should be used in a thread
begin
while SlideshowOn = true do
begin
SlideshowInterval := MainForm.SlideshowIntervalUpDown.Position*1000;
Sleep(SlideshowInterval);
NextImage; //NextImage calls LoadImage which should be a thread
if CurrentPos = CurrentDirImageCount-1 then SlideshowOn := false;
end;
end;
function MainImageIsEmpty;
begin
if MainForm.MainImage.Picture = nil then MainImageIsEmpty := true
else MainImageIsEmpty := false;
end;
procedure TMainForm.NextButtonClick(Sender: TObject);
begin
NextImage;
end;
procedure TMainForm.PrevButtonClick(Sender: TObject);
begin
PrevImage;
end;
procedure TMainForm.StartButtonClick(Sender: TObject);
begin
FirstImage;
end;
procedure TMainForm.EndButtonClick(Sender: TObject);
begin
LastImage;
end;
procedure TMainForm.SlideshowButtonClick(Sender: TObject);
begin;
if SlideshowOn = false then
begin
SlideshowOn := true;
SlideshowThread := BeginThread(nil, 0, Addr(Slideshow), nil, 0, SlideshowThreadID);
SlideshowButton.Caption := '||';
SlideshowButton.Hint := 'DIAVETÍTÉS LEÁLLÍTÁSA';
end
else
begin
SlideshowOn := false;
CloseHandle(SlideshowThread);
SlideshowButton.Caption := '|>';
SlideshowButton.Hint := 'DIAVETÍTÉS INDÍTÁSA';
end;
end;