Попробуйте выставить некоторую часть палитры как свойство метки, а затем ее анимировать:
Q_PROPERTY(QColor color READ color WRITE setColor)
void MyLabel::setColor(const QColor &value) {
QPalette palette;
palette.setBrush(QPalette::WindowText, value);
setPalette(palette);
}
QColor MyLabel::color() {
return palette(QPalette::Normal, QPalette::Window).
}
void MyLabel::startFadeOutAnimation() {
QPropertyAnimation *animation = new QPropertyAnimation(label, "color", this);
QColor c = label->color();
animation->setKeyValueAt(0, c);
c.setAlpha(0);
animation->setKeyValueAt(1, c);
animation->setEasingCurve(QEasingCurve::OutQuad);
animation->setDuration(1000);
animation->start(QAbstractAnimation::DeleteWhenStopped);
}
Вы можете попытаться избежать подкласса путем определения и регистрации нового интерполятора, который будет обрабатывать QPalette qRegisterAnimationInterpolator , но это немного сложно.
Вы можете создать семафор и остановить выполнение (поместить код в файл * .dpr) и вывести на экран запущенное приложение.
var
Semafor: THandle;
begin
{ Don't start twice ... if already running bring this instance to front }
Semafor := CreateSemaphore(nil, 0, 1, 'MY_APPLICATION_IS_RUNNING');
if ((Semafor <> 0) and { application is already running }
(GetLastError = ERROR_ALREADY_EXISTS)) then
begin
RestoreWindow('TMyApplication');
CloseHandle(Semafor);
Halt;
end;
Application.CreateForm(....);
Application.Initialize;
Application.Run;
CloseHandle(Semafor);
end;
РЕДАКТИРОВАТЬ (добавлен метод RestoreWindow
):
aFormName
- это имя вашего основного класса формы в вашем приложении.
procedure RestoreWindow(aFormName: string);
var
Wnd,
App: HWND;
begin
Wnd := FindWindow(PChar(aFormName), nil);
if (Wnd <> 0) then
begin { Set Window to foreground }
App := GetWindowLong(Wnd, GWL_HWNDPARENT);
if IsIconic(App) then
ShowWindow(App, SW_RESTORE);
SetForegroundwindow(App);
end;
end;
В прошлом я использовал сокет для предотвращения одновременной работы нескольких экземпляров. Если сокет используется, не продолжайте программу, если она доступна, пусть все работает как обычно.
Если вы хотите остановить выполнение вашего приложения более одного раза в в одно и то же время (поместите код в файл *. Dpr ] проекта). отобразит сообщение после запуска второго приложения и мгновенно остановит его.
Forms,
Unit1 in 'Unit1.pas' {Form1},
// add this units ....
TlHelp32,SysUtils,Windows,Dialogs;
{$R *.res}
function ProcessCount(const ExeName: String): Integer;
var
ContinueLoop: BOOL;
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
begin
FSnapshotHandle:= CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize:= SizeOf(FProcessEntry32);
ContinueLoop:= Process32First(FSnapshotHandle, FProcessEntry32);
Result:= 0;
while Integer(ContinueLoop) <> 0 do begin
if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
UpperCase(ExeName)) or (UpperCase(FProcessEntry32.szExeFile) =
UpperCase(ExeName))) then Inc(Result);
ContinueLoop:= Process32Next(FSnapshotHandle, FProcessEntry32);
end;
CloseHandle(FSnapshotHandle);
end;
begin
if ProcessCount(ExtractFileName(Application.ExeName)) > 1 then begin
MessageDlg('Application is already running!', mtError, [mbOK], 0);
Application.Terminate;
end else begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end;
end.
См. Этот блок (с помощью CreateMutex): UiApp
Кроме того, на этой странице вы можете прочитать о преимуществах и недостатках этой работы с различными методами (mutex, FindWindows, ..). .).
У этого устройства есть решение, позволяющее активировать предыдущий экземпляр приложения при его обнаружении.
С уважением и извините за мой плохой английский.
Нефтали-Герман Эстевес-
Я бы сказал, что есть несколько разных стратегий, которые вы можете использовать. Но самый простой (и не зависящий от платформы) - это тот, который вы сами предложили, а именно, при запуске программы проверить, есть ли файл блокировки, созданный в заданном, определенном месте. Если этот файл блокировки существует, значит, другой экземпляр уже запущен, если он не существует, значит, другой экземпляр не запущен. Когда ваша программа завершает работу, вы удаляете файл блокировки.
Однако, при использовании этой стратегии у вас есть другая проблема, что произойдет, если ваша программа потерпит крах? Файл блокировки все еще остается, и этот конкретный случай должен быть обработан.
Другой стратегией является общесистемное решение мьютекса, где вы регистрируете свое присутствие в операционной системе (или также возможно, что это делается автоматически). Когда второй экземпляр затем пытается запуститься, он проверяет, существует ли уже активный процесс с определенным идентификатором. Если он уже существует, второй процесс выбирает не запускаться и, по выбору, фокусирует окно первого процесса (если рассматриваемый процесс имеет окно, которое есть).
Тем не менее, эта стратегия зависит от платформы, и реализация будет отличаться от платформы к платформе.
Я хотел бы добавить один момент к превосходному ответу Роба Кеннеди (кроме того факта, что было бы лучше сделать функцию из его кода, а не копировать все в файл DPR Вам нужны только два параметра, имя мьютекса и логическое значение, должен ли мьютекст быть индивидуальным или общесистемным).
Ответ не уделяет особого внимания наименованию мьютекса. Если вы ожидаете, что ваша программа будет установлена с помощью Inno Setup (и, возможно, других инструментов настройки), вы должны тщательно выбрать имя, так как мьютекс можно использовать, чтобы программа установки проверила, запущено ли приложение в данный момент, и предупредила они должны закрыть все экземпляры приложения. Если вы решите разрешить один экземпляр программы для каждого пользователя, вам может потребоваться также создать второй общесистемный мьютекс, поскольку для установки может не потребоваться никаких запущенных экземпляров приложения вообще , чтобы в состоянии заменить файлы. Имя, которое будет использоваться для синхронизации с установщиком InnoSetup, должно быть жестко запрограммировано.
Всемогущий JVCL имеет компонент для этой цели. Смотрите "TJvAppInstances".
Вы можете просто использовать функцию API окон FindWindow. В delphi имя класса окна совпадает с именем класса, вы можете переопределить имя класса, переопределив функцию CreateParams. Чтобы проверить, существует ли окно, добавьте код перед созданием главного окна, перед Application.Initialize;
Program test
var
handle :HWND;
begin
handle := FindWindow('TMySuperApp', nil);
if IsWindow(handle) then
begin
//app is running
exit;
end.
Application.Initialize;
Application.CreateForm(TMySuperApp, SuperApp);
Application.Run;
end;
Вы создаете систему мьютекс .
У меня нет кода Delphi, но вот код C ++:
HANDLE Mutex;
const char MutexName[] = "MyUniqueProgramName";
Mutex = OpenMutex(MUTEX_ALL_ACCESS, false, MutexName);
if (Mutex)
throw Exception("Program is already running.");
else
Mutex = CreateMutex(NULL, true, MutexName);
Нормальным решением является создание именованного общесистемного мьютекса .
Я не предоставил код, поскольку я не знаю Delphi. Я могу предоставить код C #, если это будет полезно.
Как впервые предположил Джон, вы можете попробовать создать мьютекс. Звоните CreateMutex
. Если вы получите ненулевой дескриптор, позвоните GetLastError
. Он скажет вам, был ли вы тем, кто создал мьютекс, или мьютекс уже был открыт ранее (Error_Already_Exists
). Обратите внимание, что не необходимо приобретать право собственности на мьютекс. Мьютекс не используется для взаимного исключения. Он используется, потому что это именованный объект ядра. Событие или семафор тоже могут сработать.
Техника мьютекса дает логический ответ: да, есть другой экземпляр, или нет, нет.
Вы часто хотите знать больше, чем просто это. Например, вы можете захотеть узнать дескриптор главного окна другого экземпляра, чтобы вы могли указать, чтобы он вышел на передний план вместо вашего другого экземпляра. Вот где файл с отображением в памяти может пригодиться; он может содержать информацию о первом экземпляре, чтобы более поздние экземпляры могли ссылаться на него.
Будьте осторожны при выборе названия мьютекса. Внимательно прочитайте документацию и помните, что некоторые символы (например, обратная косая черта) недопустимы в некоторых версиях ОС, но необходимы для определенных функций в других версиях ОС.
Также помните о проблеме других пользователей. Если ваша программа может быть запущена через удаленный рабочий стол или с помощью быстрого переключения пользователей, то могут быть другие пользователи, уже запускающие вашу программу, и вы, возможно, не захотите ограничивать текущий запуск программы вашим пользователем. В этом случае не используйте глобальное имя. Если вы действительно хотите ограничить доступ для всех пользователей, то убедитесь, что атрибуты безопасности объекта мьютекса таковы, что каждый сможет открыть для него дескриптор. Использование нулевого указателя для параметра lpSecurityAttributes
недостаточно для этого; «дескриптор безопасности по умолчанию», который упоминает MSDN, предоставляет полный доступ текущему пользователю и не дает доступа другим.
Вам разрешено редактировать файл DPR вашей программы. Обычно это хорошее место для подобных вещей. Если вы подождете до события OnCreate
одной из ваших форм, то ваша программа уже наберет импульс для нормального запуска, поэтому неуместно пытаться завершить программу в этот момент. Лучше прекратить работу до того, как будет выполнена слишком большая работа с пользовательским интерфейсом Например:
var
mutex: THandle;
mutexName: string;
begin
mutexName := ConstructMutexName();
mutex := CreateMutex(nil, False, PChar(mutexName));
if mutex = 0 then
RaiseLastOSError; // Couldn't open handle at all.
if GetLastError = Error_Already_Exists then begin
// We are not the first instance.
SendDataToPreviousInstance(...);
exit;
end;
// We are the first instance.
// Do NOT close the mutex handle here. It must
// remain open for the duration of your program,
// or else later instances won't be able to
// detect this instance.
Application.Initialize;
Application.CreateForm(...);
Application.Run;
end.
Существует вопрос о том, когда закрывать дескриптор мьютекса. Вам не нужно закрывать это. Когда ваш процесс окончательно завершается (даже если происходит сбой), ОС автоматически закрывает все оставшиеся дескрипторы, а когда больше нет открытых дескрипторов, объект мьютекса будет уничтожен (что позволит запустить другой экземпляр вашей программы и считать себя быть первым экземпляром).
Но вы все равно можете закрыть ручку. Предположим, вы решили реализовать функцию SendDataToPreviousInstance
, о которой я упоминал в коде. Если вы хотите стать модным, то вы можете учесть, что предыдущий экземпляр уже закрывается и не может принять новые данные. Тогда вам не захочется закрывать второй экземпляр. Первый экземпляр может закрыть дескриптор мьютекса, как только узнает, что завершает работу, и фактически становится экземпляром «хромой утки». Второй экземпляр попытается создать дескриптор мьютекса, преуспеть и считать себя настоящим первым экземпляром. Предыдущий экземпляр будет закрыт непрерывно. Используйте CloseHandle
, чтобы закрыть мьютекс; вызывайте его из обработчика событий вашей основной формы OnClose
или откуда угодно, например, Application.Terminate
.