LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

move panel between monitors without losing data?

Solved!
Go to solution

I've read here and here for help.  The best I can deduce is to call SetSystemAttribute and change the ATTR_DEFAULT_MONITOR value before doing a LoadPanel followed by a DisplayPanel.  Then to change the panel from that monitor to another, I must call a DiscardPanel, then repeat the whole process. 

 

This successfully changes my panel from one to a second monitor.

 

However, as soon as I discard the panel, all the control data on that panel gets wiped clean.  So how to change a panel from monitor to monitor without losing its data?

 

According to the help:

 

To open panels on another monitor, call SetSystemAttribute to change the value of ATTR_DEFAULT_MONITOR before you call LoadPanel or DisplayPanel.

I have found this not exactly to be the case.  Repeat calling of the DisplayPanel just redraws the panel.  Again, I'd have to close the panel first.  I tried a HidePanel instead, but that's not working.


On my panel, I have a table of data.  I need this to stay resident while switching the panel between monitors.  Ideas?

0 Kudos
Message 1 of 9
(4,700 Views)

I have no experience with multi monitor systems, but I suppose you could rely on SavePanelState and RecallPanelState to properly handle transferring data from the old to the new panel. Be aware of the problem discussed in this thread: the thread is quite old and I don't know if this issue has been solved in recent versions of CVI; if not, you may need to store somewhere the number of lines in the old table control, then load the new panel, add the lines to the table and finally restore values in the new panel.



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 2 of 9
(4,694 Views)

Thanks, Roberto.  We are so close!  Here is the code I'm using to test this.  I have a switch control located on another panel which is not saved.  I am trying to save my "mainPanel" and its table data.

 

case EVENT_COMMIT:
	// first preserve the data in the panel
	GetNumTableRows(mainPanel,MAIN_TABLE,&tableRows);
	SavePanelState(mainPanel,"backup.panel",0);
	
	// next trash the main panel
	DiscardPanel(mainPanel);
	
	// switch is configured for 0 = Here, 1 = There
	GetCtrlVal(panel,control,&switchVal);
	switch (switchVal)
	{
		case TRUE: 	
			// change monitor
			SetSystemAttribute(ATTR_DEFAULT_MONITOR, 1);
			mainPanel = LoadPanel(0, "Panels.uir", MAIN);

			DisplayPanel(mainPanel);
			InsertTableRows(mainPanel,MAIN_TABLE,-1,tableRows,VAL_USE_MASTER_CELL_TYPE);
			
			RecallPanelState(mainPanel,"backup.panel",0);
			break;
		
		case FALSE:
			// change monitor
			SetSystemAttribute(ATTR_DEFAULT_MONITOR, 0);
			mainPanel = LoadPanel(0, "Panels.uir", MAIN);

			DisplayPanel(mainPanel);
			InsertTableRows(mainPanel,MAIN_TABLE,-1,tableRows,VAL_USE_MASTER_CELL_TYPE);
			
			
			RecallPanelState(mainPanel,"backup.panel",0);
			break;
	}
	break;

Unfortunately, I get the NFRTE "The control values could not be loaded because the panel has changed".  I have set up some breakpoints and observed that when the panel reloads on the new monitor, the correct number of rows gets inserted first.

 

As you can see, between the time I save the state and the time I recall it again, I'm not making any direct changes to the panel.  What other activities qualify as changes?  I have two timers on mainPanel, so I also tried disabling them before the SavePanelState.  Still getting that error.  :manfrustrated:

0 Kudos
Message 3 of 9
(4,676 Views)

Yikes, I'm not the only one!

 

https://forums.ni.com/t5/LabWindows-CVI/Table-Control/m-p/1971737/highlight/true#M57627

 

I think I'm just going to save out each row of the table myself and repopulate the table after reloading the panel.

0 Kudos
Message 4 of 9
(4,671 Views)
Solution
Accepted by topic author ElectroLund

ElectroLund ha scritto:

I think I'm just going to save out each row of the table myself and repopulate the table after reloading the panel.


It could be faster to duplicate the control on a hidden panel and then duplicate it again on the new main panel.

(It's not an idea of mine: Luis has given it here)



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 5 of 9
(4,668 Views)

Great suggestion, Roberto.  This is a fast method!  In case others are interested, here is the code I settled on:

 

int CVICALLBACK CallPanel (int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
	struct sourcedata source[MAX_TABLE_SOURCES];
	int tempPanel;
	int tempTable = (int)callbackData;	// retrieve previous control ID
	int switchVal;
	
	switch (event)
	{
		case EVENT_COMMIT:
			tempPanel = NewPanel(0,"Temporary Panel",VAL_AUTO_CENTER,VAL_AUTO_CENTER,500,1000);
			tempTable = DuplicateCtrl(mainPanel,tableControl,tempPanel,0,VAL_KEEP_SAME_POSITION,VAL_KEEP_SAME_POSITION);
			
			// save the duplicated control ID for next monitor switch
			SetCtrlAttribute(panel,control,ATTR_CALLBACK_DATA,(void *)tempTable);
			
			// next trash the main panel
			DiscardPanel(mainPanel);
			
			// switch is configured for 0 = Here, 1 = There
			GetCtrlVal(panel,control,&switchVal);
			SetSystemAttribute(ATTR_DEFAULT_MONITOR,switchVal);
			
			// reinit main panel and copy over table with updated control ID
			mainPanel = LoadPanel(0, "Panels.uir", MAIN);
			tableControl = DuplicateCtrl(tempPanel,tempTable,mainPanel,0,VAL_KEEP_SAME_POSITION,VAL_KEEP_SAME_POSITION);
			
			// now done with temp panel
			DiscardPanel(tempPanel);
			
			// redisplay main
			DisplayPanel(mainPanel);
			break;
			
		default:
			return 0;
	}
	return 0;
}

 

The only caveat to this solution is that with deleting and recopying the table control each time, any other manipulation of the table control outside this function will need access to the control ID.  So rather than have the table predefined in my UIR, I elected to simply create the control programmatically in my main init function:

 

tableControl = NewCtrl(mainPanel,CTRL_TABLE,"X-Ray Sources",87,62);
SetCtrlAttribute(mainPanel,tableControl,ATTR_CALLBACK_FUNCTION_POINTER,*TableEdit);

tableControl is global, so it should be used for all other table references.

0 Kudos
Message 6 of 9
(4,661 Views)

Correction on the above...

 

Using the ATTR_CALLBACK_FUNCTION_POINTER was incorrect.  When I removed my existing table control from my UIR file, it was then necessary to create the callback function like this instead:

 

InstallCtrlCallback(mainPanel,tableControl,TableEdit,NULL);

 

 

0 Kudos
Message 7 of 9
(4,655 Views)

Old thread, but still useful, I try to add some information for a similar situation.

 

I have 2 touch monitor and for security reason, the software command a machine, i have to use only 1 monitor per time.

 

a solution that i've found on the web is this, swap the active monitor trough a windows command:

 

 

The built-in DisplaySwitch.exe tool can emulate the WindowsKey + P capabilities.
You can potentially create four desktop shortcuts so that you have one available for each option:

For the PC screen only: %windir%\System32\DisplaySwitch.exe /internal
For Duplicate: %windir%\System32\DisplaySwitch.exe /clone
For Extend: %windir%\System32\DisplaySwitch.exe /extend
For Second screen only: %windir%\System32\DisplaySwitch.exe /external

 

the other solution that i've thinked is to SetPanelAttribute and put left coordinate with offset to go on other monitor.

 

int first_monitor = {0};
int num_monitor = {0};
int primary_monitor = {0};
GetSystemAttribute (ATTR_FIRST_MONITOR, &first_monitor);
GetSystemAttribute (ATTR_NUM_MONITORS, &num_monitor);
if(num_monitor > 1)
{
GetSystemAttribute (ATTR_PRIMARY_MONITOR, &primary_monitor);
GetMonitorAttribute (primary_monitor, ATTR_WIDTH, &width);
GetPanelAttribute (panel, ATTR_LEFT, &left);
if(left < width) SetPanelAttribute (panel, ATTR_LEFT, left + width);
else if(left >= width) SetPanelAttribute (panel, ATTR_LEFT, left - width);
}

 

Davide Vittorio G. - TLGB S.R.L.
Italian SW Developer
Message 8 of 9
(2,561 Views)

Another (untested) method would be to:

- change the ATTR_DEFAULT_MONITOR

- create a temporary empty panel with CreatePanel with a Center on Load attribute, display it

- get its Left/Top

- discard it

- set the Left/Top of your panel to similar coordinates

0 Kudos
Message 9 of 9
(2,500 Views)