I have a nice UIframe dialog and would like to have a button that when pressed causes an element (i.e., small image) in the UI to blink. Pressing a second button should stop the element from blinking. Any example code available?
Thank you for your help.
There is no specific animated UI element available in DM scripting. However, I was successful with creating a 'blinking' element by using a periodic main thread to regularly alternate a bitmap element.
Here is the example code:
Class myBlinkDotDLG : UIframe
{
number onBlink
number periodicTaskID
image GetDotImage( object self, number highlight )
{
image dot := realimage( "Dot", 4,40,40)
dot = iradius < 20 ? (20-iradius) : 0
dot /= max(dot)
RGBImage colDot = highlight ? (RGB(255,180,0)*dot) : (RGB(250,80,0)*dot)
colDot = RGB( Red(colDot)+75,Green(colDot)+76,Blue(colDot)+78)
return ColDot
}
void StartBlink( object self )
{
if ( 0 == periodicTaskID )
periodicTaskID = AddMainThreadPeriodicTask( self,"BlinkToggle", 0.5 )
}
void StopBlink( object self )
{
if ( 0 != periodicTaskID )
RemoveMainThreadTask( periodicTaskID )
periodicTaskID = 0
}
void BlinkToggle( object self )
{
onBlink = !onBlink
Result( "\n Blink:" + onBlink )
taggroup dotTG = self.LookUpElement("Dot")
if ( dotTG.TagGroupisValid())
dotTG.DLGGetElement(0).DLGBitmapData(self.GetDotImage(onBlink))
else
self.StopBlink() // Important! You need to unregister the mainthread task if there is no dialog anymore
}
object CreateAndShowDialog( object self )
{
TagGroup DLG, DLGitems
DLG = DLGCreateDialog( "Test", DLGitems )
DLGitems.DLGAddElement( DLGCreateGraphic(40,40).DLGAddBitmap( self.GetDotImage(1) ).DLGIdentifier("Dot") )
DLGitems.DLGAddElement( DLGCreateLabel( "Blinking\tDot" ))
DLG.DLGTableLayout(2,1,0)
self.Init( DLG ).Display( "Blinky" )
self.StartBlink()
return self
}
}
Alloc( myBlinkDotDLG ).CreateAndShowDialog()
Note that the registered periodic task will keep the UIframe object in scope, even if the dialog window is closed.
However, the LookupElement() command will not return a valid TagGroup when the dialog window no longer exists, so I have used this to check for this condition and automatically unregister the task, should it still be running.
My example code doesn't have a button to start/stop the blinking, but that would be straight forward to add. Just have the according action methods call StartBlink and StopBlink
Related
Im trying to create a generator, when i click the start button, the textview gives me a a random number.
This works great,
but i want to show the numbers still i click stop
so it must give me every few secons a new random number from my mutable List.
How could I do this?
My first opinion was, recursive function.
private fun run() {
var x = 0
var randomListe = mutableListOf<Int>()
randomListe.add(Random.nextInt())
x++
for (element in randomListe) {
var x = 0
val zahlInListe = randomListe[x]
// Thread.sleep(1_000)
// I tried while here
textView.text = ("${randomListe[x]} \n das ist die Random Zahl moruk\n")
}
}
You can launch a coroutine block in lifecycleScope for this and you can also remove redundant x for keeping the current index to show the last value, you can use the last method of ArrayList to get the last value in the list
Make changes to your run method and return a Job object from it so you can cancel it later when the user taps on the stop button.
private fun run(): Job {
return lifecycleScope.launch {
while(true){
delay(1000)
var randomListe = mutableListOf<Int>()
randomListe.add(Random.nextInt())
textView.text = ("${randomListe.last()} \n das ist die Random Zahl moruk\n")
}
}
}
Now keep the return job value in a variable, on calling the run method
private var job: Job? = null
job = run()
Call cancel on job when the user taps on the stop button
btnStop.setOnClickListener{
job?.cancel()
}
I would like a create a box with the field being update by another function (a loop in my example).
Each time value changes, the display should change.
My first attempt create a box which displays only the last value.
Class MyTestDialog: UIFrame
{
TagGroup CreateMyDialog(Object self)
{
TagGroup DialogTG = DLGCreateDialog("useless text")
TagGroup Fields,FieldItems
Fields = DLGCreateBox("Current",FieldItems)
FieldItems.DLGAddElement(DLGCreateRealField(42,15,3).DLGIdentifier("#RField"))
DialogTG.DLGAddElement(Fields.DLGTableLayOut(3,1,0))
Return DialogTG
}
Void Doit(Object self,number count)
{
for (count=0; count<5; count++)
{
self.LookUpElement("#RField").DLGValue(sum(getfrontimage())*count)
self.Display("Text at top of box window")
sleep(3)
}
}
Object Init(Object self) return self.super.Init(self.CreateMyDialog())
}
Object MyBeamCurrentDisplay = Alloc(MyTestDialog).Init()
MyBeamCurrentDisplay.Display("Text at top of box window")
sleep(3)
MyBeamCurrentDisplay.Doit(5)
Your script actually works (except you don't want to re-display the dialog each time.), but because both the DM-script itself, as well as UI updating calls are handled on DM's main thread, you don't see an updated.
The dialog gets updated 5times, but the display of the dialog only gets updated once your script has finished, giving the main-thread CPU cycles to handle the UI update.
If you put your script on a background-thread, you will see it works:
// $BACKGROUND$
Class MyTestDialog: UIFrame
{
TagGroup CreateMyDialog(Object self)
{
TagGroup DialogTG = DLGCreateDialog("useless text")
TagGroup Fields,FieldItems
Fields = DLGCreateBox("Current",FieldItems)
FieldItems.DLGAddElement(DLGCreateRealField(42,15,3).DLGIdentifier("#RField"))
DialogTG.DLGAddElement(Fields.DLGTableLayOut(3,1,0))
Return DialogTG
}
Void Doit(Object self,number count)
{
for (count=0; count<5; count++)
{
self.LookUpElement("#RField").DLGValue(sum(getfrontimage())*count)
// self.Display("Text at top of box window")
sleep(0.1)
}
}
Object Init(Object self) return self.super.Init(self.CreateMyDialog())
}
Object MyBeamCurrentDisplay = Alloc(MyTestDialog).Init()
MyBeamCurrentDisplay.Display("Text at top of box window")
sleep(0.2)
MyBeamCurrentDisplay.Doit(20)
(The first line of the code has to be exactly // $BACKGROUND )
An alternative way is to keep the script on the main thread, but add doEvents() before your sleep(0.1) to give some CPU cycles to the main application finishing queued task like updating UI's.
I also played a bit with updates and different threads and thought you might find the following example useful:
number TRUE = 1
number FALSE = 0
Class MyTestDialog: UIFrame
{
object messageQueue
object stoppedSignal
number updateTimeSec
void OnUpdateStateChanged(object self, taggroup checkboxTG )
{
if ( TRUE == checkboxTG.DLGGetValue() )
self.StartThread( "RegularFrontImageUpdate" )
else
messageQueue.PostMessage( Alloc(object) ) // Single message: Stop, so any object will do
}
number AboutToCloseDocument( object self, number verify )
{
result("\nAbout to close")
messageQueue.PostMessage( Alloc(object) ) // Single message: Stop, so any object will do
stoppedSignal.WaitOnSignal( infinity(), NULL ) // Ensure we don't kill the UI object while the update thread is still accessing it
return FALSE
}
TagGroup CreateMyDialog(Object self)
{
TagGroup DialogTG = DLGCreateDialog("useless text")
TagGroup Fields,FieldItems
Fields = DLGCreateBox("Current Front Image Sum",FieldItems)
FieldItems.DLGAddElement(DLGCreateStringField("No front image",30).DLGIdentifier("#StrField").DLGEnabled(FALSE))
FieldItems.DLGAddElement(DLGCreateRealField(0,15,3).DLGIdentifier("#RField").DLGEnabled(FALSE))
FieldItems.DLGAddElement(DLGCreateCheckbox("Update", TRUE, "OnUpdateStateChanged").DLGIdentifier("#ToggleCheck"))
DialogTG.DLGAddElement(Fields.DLGTableLayOut(3,1,0))
Return DialogTG
}
void RegularFrontImageUpdate(Object self)
{
stoppedSignal.ResetSignal()
self.SetElementIsEnabled("#RField",TRUE)
self.SetElementIsEnabled("#StrField",TRUE)
result("\nEnabling auto-update")
while(TRUE)
{
object message = messageQueue.WaitOnMessage( updateTimeSec, null )
if ( message.ScriptObjectIsValid() )
{
// Handle message. We just use 'empty' objects in this example
// as we only have a "single" message: Stop updating now!
self.LookUpElement("#ToggleCheck").DLGValue( 0 )
self.LookUpElement("#RField").DLGValue( 0 )
self.SetElementIsEnabled("#RField",FALSE)
self.SetElementIsEnabled("#StrField",FALSE)
result("\nDisabling auto-update")
break;
}
else
{
// Timeout of waiting time. Just update
image front := FindFrontImage()
if ( front.ImageIsValid() )
{
self.LookUpElement("#RField").DLGValue( sum(front) )
self.LookUpElement("#StrField").DLGValue( front.GetName() )
}
else
self.LookUpElement("#ToggleCheck").DLGValue( 0 )
}
}
stoppedSignal.SetSignal()
}
object Launch(object self)
{
updateTimeSec = 0.1
messageQueue = NewMessageQueue()
stoppedSignal = NewSignal( false )
self.Init(self.CreateMyDialog()).Display("Title")
self.StartThread( "RegularFrontImageUpdate" )
return self
}
}
Alloc(MyTestDialog).Launch()
My main CDialog sometimes displays a child modeless dialog like this:
It is only displayed if the user has configured it to automatically display.
It gets displayed via the main dialogs OnInitDialog where this function is called:
void CChristianLifeMinistryEditorDlg::DisplayAssignHistoryDialog()
{
BOOL bShowAssignHistoryDialog;
bShowAssignHistoryDialog = theApp.GetNumberSetting(_T("Options"), _T("SM_ShowAssignHist"), TRUE);
if (bShowAssignHistoryDialog)
{
m_pAssignHistoryDlg = std::make_unique<CAssignHistoryDlg>(); // .release();
if (m_pAssignHistoryDlg != nullptr)
{
m_pAssignHistoryDlg->SetAssignHistMap(&m_mapSPtrHist, &m_HistoryOriginal);
m_pAssignHistoryDlg->Create(IDD_DIALOG_ASSIGN_HISTORY, this);
m_pAssignHistoryDlg->ShowWindow(SW_SHOWNORMAL);
m_pAssignHistoryDlg->UpdateWindow();
m_pAssignHistoryDlg->EnableTree(false);
}
}
}
WhatI have noticed is that some of my main windows ACCELERATOR hotkey keys don't always work. I then realised that this is because the popup window has the focus. If i single click anywhere on the main dialog to give it focus, then my accelerator hotkeys function.
Is there any way that we can easily still allow the main editor to process it's hotkeys even though the modeless window might have focus? A standard way to cater for this?
The main window handles the accelerators like this:
BOOL CChristianLifeMinistryEditorDlg::PreTranslateMessage(MSG * pMsg)
{
if (m_hAccelTable)
{
if (::TranslateAccelerator(GetSafeHwnd(), m_hAccelTable, pMsg))
return TRUE;
}
}
And the popup modeless window also makes use of PreTranslateMessage (incase it is relevant):
BOOL CAssignHistoryDlg::PreTranslateMessage(MSG* pMsg)
{
BOOL bNoDispatch{}, bDealtWith = bDealtWith = FALSE ;
if ( (pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP ||
pMsg->message == WM_CHAR)
&& pMsg->wParam == VK_RETURN)
{
// Eat it.
bNoDispatch = TRUE ;
bDealtWith = TRUE ;
}
if (!bDealtWith)
bNoDispatch = CResizingDialog::PreTranslateMessage(pMsg);
return bNoDispatch ;
}
I would pass your m_hAccelTable from CChristianLifeMinistryEditorDlg to CAssignHistoryDlg and add this to the beginning of CAssignHistoryDlg::PreTranslateMessage:
if (m_hAccelTable)
{
if (::TranslateAccelerator(GetParent()->GetSafeHwnd(), m_hAccelTable, pMsg))
return TRUE;
}
I have simple MFC dialog type window that calls external dll function and one of parameters is callback function. Callback function creates another dialog window if it is not created and updates label with information from function parameter:
int userNotify ( int iNotificationType, char* pcNotificationText )
{
if(statusDlg)
{
if ( !(statusDlg->IsWindowVisible()) )
{
statusDlg->ShowWindow(SW_SHOW);
}
statusDlg->showNotification(iNotificationType,pcNotificationText);
} else
{
statusDlg = new StatusDlg(NULL);
statusDlg->Create(StatusDlg::IDD,CWnd::GetDesktopWindow());
statusDlg->ShowWindow(SW_SHOW);
statusDlg->showNotification(iNotificationType,pcNotificationText);
}
return 0;
}
statusDlg is global variable and is very simple MFC dialog form with one static label. And it has one feature - it is placed on topmost.
BOOL StatusDlg::OnInitDialog()
{
staticStatus = (CStatic*)GetDlgItem(IDC_STATIC_TRN_STATUS_DIALOG);
...
SetWindowPos(&this->wndTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
return TRUE; // return TRUE unless you set the focus to a control
}
Dialog form is shown during callback and label shows required information. But if I try to move form with mouse it becomes like frozen like in picture below and information on label is not updated anymore. Why this happens? How to solve this problem?
When you create StatusDlg you give it a parent of the Desktop. That is very likely wrong and leads to your later issues. The parent of your second dialog should be the main dialog that invokes it.
int userNotify ( CWnd *pParentWnd, int iNotificationType, char* pcNotificationText )
{
...
statusDlg->Create(StatusDlg::IDD, pParentWnd);
...
}
The parent window pointer will simply be the this pointer when you call userNotify.
I am having a property sheet where I had four pages .In the second page I am having a list control and a button.And in the second page I created two threads .When I click next in the first page,I am trying enumerating the list control with some values which are being retrieved from the network .So,here the a search dialog and enumerating the list are being handled in two different threads which runs in parallel.On front of the page the search dialog got popped up and background the values from network are retrieved and list is getting enumerated with those values.During that time if I click on the client area then this search dialog is getting minimized.But this should not happen until unless the search dialog is dismissed I must not be given access to the parent window(same scenario as ModalDiaolg box,as we know until unless the child window is closed we will not be able to access the parent right,similarly scenario is required for me.)this is the code I had done for getting those threads to be run at a time.
BOOL CModelSelectionView::CreateModelThread()
{
unsigned threadID;
if( NULL == ( m_hModelThread = (HANDLE)_beginthreadex(
NULL,
0,
&CModelSelectionView::ModelThreadProc,
reinterpret_cast<void*>(this),
0,
&threadID)) )
{
return FALSE;
}
return TRUE;
}
//this thread is for search dialog
UINT CModelSelectionView::ModelThreadProc( void* lpContext )
{
CModelSelectionView *pSelectModelFromList =
reinterpret_cast<CModelSelectionView*> (lpContext);`
AfxSetResourceHandle(theApp.m_hDialogResource);
CSearchingView SearchView(IDD_DIALOG_SEARCH);
INT nRes = SearchView.DoModal();
::CloseHandle( pSelectModelFromList->m_hModelThread );
pSelectModelFromList->m_hModelThread = NULL;
_endthreadex( 0 );
return TRUE;
}
BOOL CModelSelectionView::CreateInstallerThread()
{
unsigned threadID;
if( NULL == ( m_hInstallerThread = (HANDLE)_beginthreadex(
NULL,
0,
&CModelSelectionView::InstallerThreadProc,
reinterpret_cast<void*>(this),
0,
&threadID)) )
{
return FALSE;
}
return TRUE;
}
//Second thread for Initializing the list with some values
UINT CModelSelectionView::InstallerThreadProc( void* lpContext )
{
CModelSelectionView *pSelectModelFromList =
reinterpret_cast<CModelSelectionView*> (lpContext);
pSelectModelFromList->m_listCtrl.DeleteAllItems();
LVITEM lvitem;
lvitem.mask = LVIF_TEXT;
lvitem.iItem = 0;
lvitem.iSubItem = 0;
lvitem.pszText = L"";
lvitem.cchTextMax = sizeof(lvitem.pszText);
int nItem = pSelectModelFromList->m_listCtrl.InsertItem(&lvitem);
::Sleep(200);
pSelectModelFromList->m_listCtrl.SetItemText(0,1,L"XXX");
pSelectModelFromList->m_listCtrl.SetItemText(0,2,L"YYY");
pSelectModelFromList->m_listCtrl.SetItemText(0,3,L"ZZZ");
pSelectModelFromList->m_listCtrl.SetItemText(0,4,L"AAAA");
::Sleep(200);
::TerminateThread(pSelectModelFromList->m_hModelThread, 0);
::CloseHandle(pSelectModelFromList->m_hModelThread );
pSelectModelFromList->m_hModelThread = NULL;
::CloseHandle( pSelectModelFromList->m_hInstallerThread );
pSelectModelFromList->m_hInstallerThread = NULL;
_endthreadex( 0 );
return TRUE;
}
Until unless the search dialog is closed it should not be allowed to access the parent window.For instance when click a button and for that button handler I was calling domodal then a child dialog pop-up appears until unless we dismiss that dialog we will not be allowed to access the parent right ,similarly I have to get in this scenario.
Can anyone suggest me how can I achieve that.
Can anyone please suggest me how
Simply EnableWindow(FALSE) for the window that should not receive any input. It will still be displayed and its contents is updated, but mouse and keyboard events will not reach this window.