How to create a multi-thread window

This forum is for eXpress++ general support.
Message
Author
skiman
Posts: 1199
Joined: Thu Jan 28, 2010 1:22 am
Location: Sijsele, Belgium
Contact:

Re: How to create a multi-thread window

#21 Post by skiman »

Hi Roger, Brian,

In the screenshots posted, there was the following sample by Wolfgang Ciriack.

Image

If you look at the main menu, I was wondering when a user clicks on Kunden (customers) and when this is open, he needs to create a new invoice (Rechnung). With my application a user can open the 'invoices thread' 5 times. In a concept as in this screenshot, this looks almost impossible.
Best regards,

Chris.
www.aboservice.be

bwolfsohn
Posts: 649
Joined: Thu Jan 28, 2010 7:07 am
Location: Alachua, Florida USA
Contact:

Re: How to create a multi-thread window

#22 Post by bwolfsohn »

skiman wrote:Hi Roger, Brian,

In the screenshots posted, there was the following sample by Wolfgang Ciriack.

If you look at the main menu, I was wondering when a user clicks on Kunden (customers) and when this is open, he needs to create a new invoice (Rechnung). With my application a user can open the 'invoices thread' 5 times. In a concept as in this screenshot, this looks almost impossible.
In our application, we only let the user open a dialog once. If you wanted the ability to open the invoice window 5 times, roger would need to add dynamic tabbing to the multi-thread window. i.e. add another invoice tab (invoice(2)) each time the invoice tab is opened... then remove the tab when the invoice tab is closed.

If we tried to do that with our users, we'd have 3 dozen buyer windows open, and they'd all be in the middle of editing the same buyer!! :)
Brian Wolfsohn
Retired and traveling around the country to music festivals in my RV.
OOPS.. Corona Virus, so NOT traveling right now...
http://www.breadmanrises.com
FB travel group: The Breadman Rises

Wolfgang Ciriack
Posts: 484
Joined: Wed Jan 27, 2010 10:25 pm
Location: Berlin Germany

Re: How to create a multi-thread window

#23 Post by Wolfgang Ciriack »

a concept as in this screenshot, this looks almost impossible.
Why should that be impossible ?
Invoices starts a new thread, that is a new window in the windows taskbar.
If you starts it 5 times you have 5 windows with invoices in the windows taskbar.
_______________________
Best Regards
Wolfgang

User avatar
Tom
Posts: 1234
Joined: Thu Jan 28, 2010 12:59 am
Location: Berlin, Germany

Re: How to create a multi-thread window

#24 Post by Tom »

If you starts it 5 times you have 5 windows with invoices in the windows taskbar.
You don't need to create 5 independent windows with taskbar presence. A thread is just a capsuled process where something happens inside. It has connections to the application framework, to global objects, statics and, of course, the database. The advantage of a thread-based model is that the app doesn't need to care whether a table is already opened outside the thread or not, since every thread comes with it's own workspace and workareas. If some logic is created, like a dialog showing some data, it can easily be moved into a thread and used x times with no other changes. If the application is able to deal shared tables, it can be multi-threaded aswell, since this is almost the same.

But it's not a must to place the logic running in a thread in an own dialog (window). A threaded dialog can have a parent in another thread. If you do something like this:

Code: Select all

@ 0,0 DSAY .. GET ...
... more dialog elements

@ 30,0 DCSAY '' SIZE 8,1 OBJECT oTime

DCREAD GUI EVAL {||SetTimerEvent(100,{||ShowTime(oTime)})}

FUNCTION ShowTime(oTime)
oTime:SetCaption(Time())
RETURN NIL
You have a new thread where "ShowTime()" runs. "oTime", which is a static on the main dialog, is known by that thread. It updates the caption of this object from inside a different thread. This is possible since there is only one UI-thread which manages the screen output. It doesn't care for from where it's called from. If it's able to access the object which is to be updated, it updates the object.

Something quite similar could be done with complex dialogs. You can place elements on a dialog and use them as parents for other dialogs created in different threads. Look at this simple code:

Code: Select all

#include "dcdialog.ch"
#include "thread.ch"
#pragma library ("dclipx.lib")

FUNCTION Main()
LOCAL GetList := {}, aTabs := Array(3), nCounter := 0, aThreads := Array(3)

@ 0,0 DCTABPAGE aTabs[1] CAPTION 'Test 1' SIZE 115,25
@ 0,0 DCTABPAGE aTabs[2] CAPTION 'Test 2' RELATIVE aTabs[1]
@ 0,0 DCTABPAGE aTabs[3] CAPTION 'Test 3' RELATIVE aTabs[2]

@ 27,0 DCPUSHBUTTON CAPTION 'New dialog' SIZE 12,1 ;
       ACTION {||aThreads[ThreadCounter()+1] := Thread():New(),;
       aThreads[ThreadCounter()+1]:Start({||NewDialog(aTabs,ThreadCounter(1))}),SetAppFocus(aTabs[ThreadCounter()])} ;
       WHEN {||ThreadCounter() < 3}

DCREAD GUI FIT ADDBUTTONS TITLE 'Test'

RETURN NIL

PROC AppSys() ; RETURN

FUNCTION ThreadCounter(nInc)
STATIC nThreadCount := 0
IF PCount()>0
  nThreadCount += nInc
ENDIF
RETURN nThreadCount


FUNCTION NewDialog(aTabs,nCounter)
LOCAL GetList := {},cText := Space(30)

@ 2,1 DCSAY 'Test '+Str(nCounter,1,0)+':' GET cText

DCREAD GUI PARENT @aTabs[nCounter]

RETURN NIL
It shows 3 tabpages with nothing on them at start. If you click "New dialog", the next free tabpage is used as the parent for a new dialog running in a different thread. This is not very complex, it might be not very useful, but it works. The four dialogs are completely independent.
Best regards,
Tom

"Did I offend you?"
"No."
"Okay, give me a second chance."

User avatar
rdonnay
Site Admin
Posts: 4813
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

Re: How to create a multi-thread window

#25 Post by rdonnay »

Here is an update to the original sample program.
This works much better because it only starts the child thread the first time a tabpage receives focus.
Much nicer and faster.

Code: Select all

#INCLUDE "dcdialog.CH"
#INCLUDE "appevent.CH"

FUNCTION Main()

LOCAL GetList[0], oMenu, oSubMenu, oDlg, GetOptions

DCMENUBAR oMenu
  DCSUBMENU oSubMenu PROMPT 'File' PARENT oMenu
    DCMENUITEM 'Multi-Threaded Window' ;
      ACTION {|o|o := Thread():new(),Sleep(5),o:start({||MultiTab(oDlg:drawingArea)})} ;
      PARENT oSubMenu
    DCMENUITEM 'Single-Threaded Window' ;
      ACTION {|o|o := Thread():new(),Sleep(5),o:start({||DataBrowse(oDlg:drawingArea)})} ;
      PARENT oSubMenu

DCGETOPTIONS WINDOWWIDTH 1000 WINDOWHEIGHT 800
DCREAD GUI TITLE 'Multi-Threaded Window Test' PARENT @oDlg OPTIONS GetOptions

RETURN nil

* ----------

FUNCTION MultiTab(oParent)

LOCAL GetList[0], oTab1, oTab2, oTab3, aGetList[0], i, oDlg, oGetList

AAdd(aGetList,{})
AAdd(aGetList,{})
AAdd(aGetList,{})

DC_LoadRdds()

@ 0,0 DCTABPAGE oTab1 SIZE 100,25 CAPTION 'Tab 1' ;
      GOTFOCUS {|a,b,o|IIF(Empty(oTab1:childList()),;
       (o := Thread():new(), Sleep(10), o:start({||DataBrowse(oTab1,aGetList[1])})),nil)}

@ 0,0 DCTABPAGE oTab2 CAPTION 'Tab 2' RELATIVE oTab1 ;
      GOTFOCUS {|a,b,o|IIF(Empty(oTab2:childList()),;
       (o := Thread():new(), Sleep(10), o:start({||DataBrowse(oTab2,aGetList[2])})),nil)}

@ 0,0 DCTABPAGE oTab3 CAPTION 'Tab 3' RELATIVE oTab2 ;
      GOTFOCUS {|a,b,o|IIF(Empty(oTab3:childList()),;
       (o := Thread():new(), Sleep(10), o:start({||DataBrowse(oTab3,aGetList[3])})),nil)}

DCREAD GUI FIT TITLE 'This is a Three-Thread Window' ;
   APPWINDOW oParent ;
   NODESTROY PARENT @oDlg ;
   SETFOCUS @oTab1

FOR i := 1 TO Len(aGetList)
  oGetList := DC_GetListObject(aGetList[i])
  IF Valtype(oGetList) == 'O'
    oGetList:isDialogActive := .f.
    Sleep(10)
  ENDIF
NEXT

oDlg:destroy()

RETURN nil

* ------------

PROC appsys ; RETURN

* ------------

FUNCTION DataBrowse(oParent,GetList)

LOCAL oBrowse

DEFAULT GetList := {}

USE \expd20\data\xtest VIA 'FOXCDX'

@ 2,2 DCBROWSE oBrowse ALIAS 'XTEST' SIZE 96,20  FONT '10.Lucida Console'

DCBROWSECOL FIELD XTEST->areacode WIDTH 10 PARENT oBrowse
DCBROWSECOL FIELD XTEST->exchange WIDTH 10 PARENT oBrowse
DCBROWSECOL FIELD XTEST->number WIDTH 10 PARENT oBrowse
DCBROWSECOL FIELD XTEST->date WIDTH 10 PARENT oBrowse
DCBROWSECOL FIELD XTEST->time WIDTH 10 PARENT oBrowse

@ 23,2 DCSAY 'This Browse is running in Thread ' + Str(ThreadID(),2) ;
       SAYSIZE 0 FONT '12.Lucida Console'

IF oParent:isDerivedFrom("DC_XbpTabPage")
  DCREAD GUI PARENT oParent WAIT 5
ELSE
  DCREAD GUI FIT APPWINDOW oParent TITLE 'This is a One-Thread Window'
ENDIF

dbCloseAll()

RETURN nil
The eXpress train is coming - and it has more cars.

Post Reply