Обзор алгоритмов сортировки
Сортировка в StringList'е лишь в два раза медленнее так называемой "быстрой" сортировки. Для сравнения, рассмотрим и некоторые другие алгоритмы сортировки. Сортировка пузырьком - самый естественный, он же самый медленный алгоритм сортировки. Массив чисел просматривается от начала до конца до тех пор, пока любые два рядом стоящих числа не будут расположены по возрастанию. Сортировка вставками - ещё один простой способ сортировки. Массив чисел сортируется с начала, и каждое последующее число вставляется в уже отсортированную часть массива на предназначенное ему место. Быстрая сортировка - очень эффективный алгоритм, и известна как в среднем самая быстрая из универсальных алгоритмов сортировки. Быстрая сортировка сравнивает все элементы массива с одним, выбранным практически наугад, элементом (опорным элементом) и тем самым делит массив на две части - в одну попадают числа меньшие опорного, а в другую - большие опорного.
Урок 13
Создание интерфейса пользователя
Создание интерфейса пользователя сводится к выбору из палитры компонентов необходимые для работы программы компоненты Delphi, служащие интерфейсом управления, а также интерфейсом отображения информации, и перенесение их на Форму с последующей компоновкой. Интерфейс пользователя, создаваемый Вами, должен использовать стандартные, привычные пользователям элементы , и обеспечивать максимальное удобство. Всё это в конечном счёте определяется таким критерием как эффективность интерфейса - максимальный результат с минимальными усилиями. Принципы создания удобного интерфейса известны. В качестве самых общих принципов при создании пользовательских интерфейсов можно рассматривать три основных положения:
Программа должна помогать выполнить задачу, а не становиться этой задачей.
При работе с программой пользователь не должен ощущать себя дураком.
Программа должна работать так, чтобы пользователь не считал компьютер дураком.
Первый принцип — это так называемая "прозрачность" интерфейса. Интерфейс пользователя должен быть интуитивно понятным, простым для освоения, и не создавать для пользователя проблем, которые он вынужден будет преодолевать в процессе работы. Используйте стандартные, без излишнего украшательства компоненты, применяйте привычные, используемые аналогичными программами приёмы управления, и Вы достигнете критериев выполнения первого принципа. Второй принцип заключается в пренебрежении интеллектуальными способностями пользователей. На собственном опыте мне известно, что часто пользователи не только не умеют работать за компьютером, но и просто боятся предпринять что-либо самостоятельно. Поэтому интерфейс пользователя должен быть максимально дружественным. Тем более, что опасения пользователей зачастую оправданны, ведь стоимость программы, да и самого компьютера не идёт ни в какое сравнение со стоимостью, например, созданной многолетними усилиями базы данных. Именно поэтому программист при создании интерфейса пользователя обязан всегда встраивать в программу "защиту от дурака" - от неправильных действий и ввода пользователем неверных данных. Но некоторые программисты чересчур увлекаются такой защитой, делают ее слишком назойливой, и в результате работа программы напоминает знаменитое "шаг влево, шаг вправо считается побегом"! И то, что программист создаёт как решение проблемы, само начинает создавать проблемы. Для соблюдения второго принципа не нужно позволять программе "исправлять" действия пользователя и указывать, что как именно ему действовать, загоняя в узкие рамки. Также не следует чрезмерно увлекаться выводом информационных сообщений-подсказок, особенно диалоговых, это отвлекает пользователя от работы. А лучше вообще предусмотреть возможность отключения подсказок. Третий принцип заключается в том, чтобы создавать программу с максимально возможными "умственными" способностями. Несмотря на быстрое развитие компьютерной техники, даже широко распространённые программы лишь весьма условно можно назвать имеющими искуственный интеллект. Они мешают работе пользователя, выводя на экран диалоговые окна с глупыми вопросами, вызывающими недоумение даже в простейших ситуациях. В результате пользователи в сердцах восклицают: "Ну и тупая же эта машина"! У меня лично вызывают раздражение постоянные вопросы практически всех текстовых редакторов о том, не сохранить ли изменённый текст, хотя первоначальный и нынешний текст не отличаются ни символом. Да, я что-то набирал, но потом вернул всё назад, неужели нельзя сообразить! Приходится проверять, не испортил ли я всё-таки что-нибудь. Старайтесь придерживаться следующих правил:
Стандартные элементы интерфейса
Небольшая палитра инструментов
Одинаковое расстояние между элементами управления
TabOrder. "Правильный" порядок
Выбор шрифтов
Выбор цветов
Альтернативное управление
Кирпичики интерфейса
Стандартные элементы интерфейса
Используйте стандартные для данного элемента интерфейса компоненты. Встретив Вашу программу, пользователь не будет терять время на знакомство, а сразу приступит к работе - это один из признаков профессионально сделанной программы.
Небольшая палитра инструментов
Старайтесь использовать не слишком большое количество разнообразных компонентов. И естественно, использовав где-то в одном месте один стандартный компонент, в аналогичном случае также применяйте именно его.
Одинаковое расстояние между элементами управления
Располагайте элементы интерфейса на одинаковом расстоянии между собой. Разбросанные как попало компоненты создают ощущение непрофессионально сделанного продукта. И наоборот, тщательно выверенное размещение на Форме кнопок, переключателей, флажков и других компонентов, составляющих интерфейс — признак качественной работы.
TabOrder. "Правильный" порядок
TabOrder - это порядок перемещения экранного курсора по элементам управления при нажатии клавиши Tab. В правильно написанной программе курсор перемещается, следуя логике работы пользователя с программой. При создании же программы программист часто меняет компоненты, одни удаляет, другие добавляет по мере необходимости. В результате в готовой программе курсор хаотично скачет по Форме. Завершив программу, не забывайте настроить TabOrder.
Выбор шрифтов
Шрифты просто оставьте в покое. Заданные по умолчанию самой Delphi шрифты подойдут для любой системы, на которой может работать Ваша программа. Полужирный шрифт используйте только для выделения важных элементов. Применение же курсива и особенно подчёркивания, которое пользователь может принять за гиперссылку - дурной тон.
Выбор цветов
Что касается цветов элементов интерфейса, то также, как и в случае со шрифтами, лучше оставьте их стандартными, по умолчанию. Delphi использует системную палитру Windows, и пользователь, изменив её, легко настроит цвета под себя.
Альтернативное управление
Профессионально сделанная программа должна иметь возможность управляться не только мышкой, но и с клавиатуры. Не должно быть функций, доступных выполнению только мышью (рисование в графических редакторах не в счёт!). Для наиболее используемых функций следует предусмотреть "горячие клавиши" для их быстрого вызова.
Кирпичики интерфейса
Что касается конкретных элементов интефейса пользователя, то качество взаимодействия пользователя с программой зависит от:
соответствия элемента управления выполняемой им задаче;
правил, по которым функционирует элемент управления. На этой страничке рассматриваются правила создания некоторых элементов интерфейса.
А сейчас хочу показать, какие инструменты предлагает Delphi для управления компонентами на Форме, их взаимным расположением и поведением курсора при нажатии клавиши Tab. Для того, чтобы расположить относительно друг друга компоненты в правильном порядке, сначала необходимо их выделить. Можно просто обвести мышкой область на Форме, в которой содержатся выбранные компоненты. Или, удерживая "Shift", указать той же мышкой каждый подлежащий выделению компонент. Повторный щелчок мышкой по выделенному компоненту (при нажатом "Shift") снимает с него выделение. Выделенными компонентами можно управлять как единым целым - передвигать по Форме, присвоить значение одинаковым свойствам, скопировать (для установки, например, на другую Форму), даже удалить.
Выделение мышкой области с компонентами |
Группа выделенных компонентов |
|
|
Теперь щёлкните правой кнопкой по одному из компонентов, и из "всплывающего" меню выберите Position -> Align... Появится диалоговое окошко, позволяющее настроить положение компонентов в группе по горизонтали и вертикали. Например, нам нужно выровнять наши четыре кнопки по левому краю и сделать так, чтобы между ними было одинаковое расстояние по вертикали. Для этого выделим радиокнопки Horizontal: Left sides и Vertikal: Space equally.
Доступ к меню выравнивания |
Диалог выравнивания компонентов |
Результат выравнивания группы компонентов |
|
|
|
Выбрав пункт Center, мы расположим компоненты так, что их центры будут располагаться на одной линии по горизонтали или вертикали, а пункт Center in window перемещает компоненты в центр окна, также по горизонтали или вертикали. В этом же меню строка Tab Order... вызывает появление диалогового окна, управляющего перемещением курсора по элементам интерфейса при нажатии клавиши Tab. В момент появления Формы на экране курсор будет находиться, естественно, на компоненте, располагающемся на первой строчке диалогового окна. И далее будет перемещаться вниз по списку. На диалоговом окне две синие стрелочки "вверх" и "вниз" управляют положением выделенного компонента. Выделяйте нужный компонент, стрелками перемещайте на нужную строчку в списке, и так далее. При выборе пункта меню Control -> появляется подменю, состоящее из двех пунктов:
Bring to Front
Send to Back
Это методы компонента, доступные также программно. Button1.SendToBack перемещает кнопку на "задний план", а Button1.BringToFront - на "передний план". То есть, если один компонент располагается над другим, эти методы меняют их местами. Случаи, в которых это может применяться, довольно очевидны.
Урок 15
Многопоточность
Потоки в Delphi выполняют функцию имитации псевдопараллельной работы приложения. Как известно, для организации многозадачности операционная система выделяет каждому приложению, выполняющемуся в настоящий момент, определённые кванты времени, длина и количество которых определяется его приоритетом. Поэтому объём работы, который приложение может выполнить, определяется тем, сколько таких квантов оно сможет получить в единицу времени. Для операционной системы каждый поток является самостоятельной задачей, которой выделяются кванты времени на общих основаниях. Поэтому приложение Delphi, умеющее создать несколько потоков, получит больше времени операционной системы, и соответственно сможет выполнить больший объём работы. Создать дополнительный поток в Delphi поможет объект TThread. Ввести объект TThread в программу можно двумя способами:
с помощью Мастера;
вручную.
1. Мастер создания дополнительного потока в Delphi создаёт отдельный модуль, в рамках которого выполняется поток. Выполним: File -> New -> Other... В появившейся табличке выбора найдём TThread Object. Появится окошко, в верхнюю строку которого (Class Name) введём имя нашего будущего потока: MyThread. В результате будет создан модуль, содержащий заготовку кода, реализующего дополнительный поток Delphi:
unit Unit2; // Имя модуля, содержащего поток. При сохранении его можно изменить. interface uses Classes; type MyThread = class(TThread) //MyThread - заданное нами имя потока. private { Private declarations } protected procedure Execute; override; end; implementation { Important: Methods and properties of objects in visual components can only be used in a method called using Synchronize, for example, Synchronize(UpdateCaption); and UpdateCaption could look like, procedure MyThread.UpdateCaption; begin Form1.Caption := 'Updated in a thread'; end; } { MyThread } procedure MyThread.Execute; begin { Place thread code here } end; end.
2. В первом способе класс MyThread был создан мастером в дополнительном модуле. Второй способ состоит в том, что мы сами создаём такой класс в рамках одного из уже существующих модулей программы, например, в модуле Unit1: unit Unit1; //Обычный модуль в котором описывается основная программа interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; //Здесь необходимо описать класс TMyThread: TMyThread = class(TThread) private { Private declarations } protected procedure Execute; override; end; var Form1: TForm1; //Нужно ввести переменную класса TMyThread MyThread: TMyThread; implementation {$R *.dfm} //Нужно создать процедуру Execute, уже описанную в классе TMyThread procedure TMyThread.Execute; begin //Здесь описывается код, который будет выполняться в потоке end;
Если поток создаётся мастером, т.е. в другом модуле, то не забудьте в основном модуле описать переменную - экземпляр потока, как указано выше. Также, поскольку класс потока описан в другом модуле, имя этого модуля необходимо добавить в секцию uses. Теперь можно запускать поток, даже если в его процедуре Execute нет ни единого оператора. //Запускать поток будем нажатием на кнопку: procedure TForm1.Button1Click(Sender: TObject); begin //Вначале нужно создать экземпляр потока: MyThread:=TMyThread.Create(False); //Параметр False запускает поток сразу после создания, True - запуск впоследствии , методом Resume //Далее можно указать параметры потока, например приоритет: MyThread.Priority:=tpNormal; //Можно указать что после завершения кода поток завершится автоматически: MyThread.FreeOnTerminate:=true; end; end. Ну вот, даже этот минимальный код позволяет поэкспериментировать с потоками и посмотреть, что они создаются в системе, работают, уничтожаются. Например, измените условие завершения потока: MyThread.FreeOnTerminate:=false; //Поток не будет уничтожен после завершения работы Теперь в Диспетчере Задач Windows можно наблюдать, что при каждом нажатии на кнопку Button1 в нашем приложении количество потоков в проекте Project1 увеличивается. А теперь поместите в процедуру Execute такой оператор: MyThread.Terminate; Метод Terminate уничтожает данный экземпляр потока. Теперь при нажати кнопки в приложении количество потоков всё равно остаётся равным 1 (это главный поток приложения), так как сразу после создания новый поток уничтожается методом Terminate, и мы просто не успеваем заметить краткий миг его существания. Тем не менее, это означает, что код потока в процедуре Execute выполняется! Ещё пример. Если в основной программе попробовать выполнить такой цикл: while True do; то приложение зависнет. А теперь поместите его в процедуру Execute. При нажатии на кнопку наш бесконечный цикл будет непрерывно выполняться в потоке, однако и приложение как целое не зависнет. При работе с потоками необходимо учитывать приоритет создаваемых потоков. Так, если в предыдущем примере запустить не один поток, а два или больше, то компьютер станет очень заметно "тормозить". Это происходит потому что приоритет по умолчанию новых потоков - нормальный. Можно уменьшить его, задав MyThread.Priority:=tpLower; Этого достаточно, чтобы компьютер чувствовал себя более свободно. Вот таблица приоритетов:
Приоритет |
Описание |
tpIdle |
Низший приоритет. Поток получает время только тогда, когда операционая система находится в состоянии простоя. |
tpLowest |
Приоритет на два пункта ниже нормального |
tpLower |
Приоритет на один пункт ниже нормального |
tpNormal |
Нормальный приоритет |
tpHigher |
Приоритет на один пункт выше нормального |
tpHighest |
Приоритет на два пункта выше нормального |
tpTimeCritical |
Максимальный приоритет. Приоритет на уровне функций ядра операционной системы. |
Вот вам готовый проект работы c потоками, для экспериментов. В продолжение темы нужно рассмотреть
