How to show updates to Illustrator IDE immediately (C++ Plugin using SDK)

Explorer ,
Jul 14, 2022 Jul 14, 2022

Copy link to clipboard

Copied

I'm developing an Illustrator plugin in C++. I'm also very new to C++ programming and relatively new to programming Illustrator.

 

I'm trying to understand how to get the IDE to show the updates as they are made. For example, I'm creating new documents, artboards and copying artwork to them all programatically, but as I use debug to step through the code after each of the functions mentioned above, I see no changes in the IDE. I have to go through the entire code to see any updates. The behavior that I'm expecting is that when my code creates a new document, add an artboard, copy artwork, they are seen immediately.

 

I'm thinking that it's timing and wondering if there is a need for threading? Best case is, it's simply a refresh of some kind that can be done to update the IDE after each function step?

 

Can anyone offer some insight on this and is my description of the issue enough for you to understand what's happening?

 

Regards,

Kazon

TOPICS
SDK

Views

200

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 3 Correct answers

Guide , Jul 16, 2022 Jul 16, 2022

I think I've run into the problem you're encountering, but I'm not 100% sure. Still, it's worth trying my workaround.

 

Basically -- at least in my case -- the problem was that I needed to let Illustrator take a beat before continuing with the save. The trick was tro create a zero duration timer, something like:

 

ai::int32 delay = 0;
AITimerHandle timer = nullptr;
auto error = sAITimer->AddTimer(pluginRef, "name_of_timeer", delay, &timer);
// check errror

 

Then you just handle the timer callback and

...

Likes

Translate

Translate
Guide , Jul 17, 2022 Jul 17, 2022

Looking at my code, it appears I neglected an important pair of lines!

 

error = sAITimer->SetTimerActive(timer, true);
THROW_EXCEP_IF(error);

I suspect that will solve your problem! Sorry I missed it!

Likes

Translate

Translate
Guide , Jul 17, 2022 Jul 17, 2022

Oh, and when you receive the timer, you should probably disable it 🙂

Likes

Translate

Translate
Explorer ,
Jul 14, 2022 Jul 14, 2022

Copy link to clipboard

Copied

Specifically what is happening is that I am generating PDFs. I am first creating the document and artboard, then copying the artwork to that new document. However, I don't see the copied artwork in the document. Using the AIActionManager suite, I am generating the PDF. I don't know for sure if it's what's causing the issue but when I get to the part of code that plays the action:

 

// Play action
error = sAIActionManager->PlayActionEvent(kAISaveDocumentAsAction, kDialogOff, vpb);
check_ai_error(error);

 

I get a blank PDF generated. What makes it even more confusing, is that if I comment out the line of code that plays the action, the artwork is then shown in the document on the artboard. I'm stepping through the code and using check_ai_error(error) throughout and there are not errors thrown, and all of my objects (variables) are showing valid with the appropriate data values.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jul 16, 2022 Jul 16, 2022

Copy link to clipboard

Copied

I think I've run into the problem you're encountering, but I'm not 100% sure. Still, it's worth trying my workaround.

 

Basically -- at least in my case -- the problem was that I needed to let Illustrator take a beat before continuing with the save. The trick was tro create a zero duration timer, something like:

 

ai::int32 delay = 0;
AITimerHandle timer = nullptr;
auto error = sAITimer->AddTimer(pluginRef, "name_of_timeer", delay, &timer);
// check errror

 

Then you just handle the timer callback and do your PDF SaveAs action there. A zero length timer is basically an 'OnIdle' event. If you had a slow enough computer, it might be twenty seconds later if that's how long it takes Illustrator to do whatever it needs to do in response to whatever you did before adding the timer, but it won't call the timer until it's done, which is what's important.

 

If you need the canvas to update in realtime, that's a different problem, but I don't think that's what you're talking about here. For that, you'd want to use AIDocumentSuite::RedrawDocument(), who's comment is:

 

Forces the current document to be redrawn. Illustrator automatically redraws
the document when a plug-in returns, so this function is not usually needed.

 

Typically, I only use that if I'm in a dialog doing somethign on the artboard with annotations, and I don't even always need it.

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 16, 2022 Jul 16, 2022

Copy link to clipboard

Copied

Hi Patterson,

 

Can't thank you enough for reaching back to me on this. While hoping to get a response, I continued looking through other posts made and your reponses to them and so went on to add some of your suggestions in those posts to my code. I will have a go with your suggestions but here is what I have now so you can get the full picture of what I've done.

 

My codebase is a hybrid I guess you can call it. I use a third party API/SDK for my UI mixed with Illustrator's API/SDK. That's why you will see references to "hdi::core".

void saveFile(ai::FilePath& filePath, AIRealRect abRect, std::auto_ptr<hdi::core::Art> &artObjRef, size_t abPrintIdx, ai::UnicodeString& artboardName, std::string fileTypeExt)
{
	ASErr error = kNoErr;

	// Create the new document to save as
	//createSaveAsDocument(abRect, artObjRef, artboardName, fileTypeExt);
	//std::thread t(&createSaveAsDocument, abRect, artObjRef, artboardName, fileTypeExt);   // t starts running
	//t.join();

	// Acquire Suites needed
	error = sSPBasic->AcquireSuite(kAIActionManagerSuite, kAIActionManagerSuiteVersion, (const void **)&sAIActionManager);
	check_ai_error(error);

	// Create new document and artboard
	// Define the document Presets
	hdi::core::Document::Preset preSet = hdi::core::Document::Preset();

	preSet.size = hdi::core::Size(abRect.right - abRect.left, abRect.top - abRect.bottom);
	preSet.colorModel = globals::g_documentColorModel;
	preSet.rulerUnits = hdi::core::RulerUnits::PixelsRulerUnits;

	if (!isPrint())
	{
		preSet.rasterResolution = preSet.RasterResolutionScreen;
	}
	else {
		preSet.rasterResolution = preSet.RasterResolutionHigh;
	}

	// Create the new document based on the defined Presets
	hdi::core::Document newDoc = hdi::core::Document(preSet, false);

	// App Idle
	applicationIdle();

	// Redraw the document to update the view
	redrawDocumentView();
	redrawDocumentView();

	hdi::core::CurrentDocument *currDoc = HDI_CORE_ILLUSTRATOR->currentDocument();

	std::auto_ptr<hdi::core::Art> newArtObj;
	
	// Duplicate the artwork
	newArtObj = artObjRef->duplicate(hdi::core::PaintOrder::PlaceAbove, NULL);

	// App Idle
	applicationIdle();

	// Redraw the document to update the view
	redrawDocumentView();
	redrawDocumentView();

	AIRealRect newAbRect;

	// Define the bounds for the new artboard
	newAbRect.left = 0.0;
	newAbRect.top = -0.0;
	newAbRect.right = round_nplaces((abRect.right - abRect.left), 0, true);
	newAbRect.bottom = round_nplaces(-(abRect.top - abRect.bottom), 0, true);

	// Center the artwork on the artboard
	transformNewArtwork(newArtObj, newAbRect, true);

	//std::thread t(&transformNewArtwork, newArtObj, newAbRect, true);   // t starts running
	//t.join();     // main thread waits for t to finish

	// App Idle
	applicationIdle();

	// Redraw the document to update the view
	redrawDocumentView();
	redrawDocumentView();

	AIActionParamValueRef vpb = NULL;
	try
	{
		error = sAIActionManager->AINewActionParamValue(&vpb);
		check_ai_error(error);

		if (vpb)
		{
			// Get the file to save the document as AI into
			ai::FilePath targetFile = getTargetFile("." + fileTypeExt, filePath, artboardName);

			// Set the target filename
			error = sAIActionManager->AIActionSetStringUS(vpb, kAISaveDocumentAsNameKey, targetFile.GetFullPath());
			check_ai_error(error);

			// App Idle
			applicationIdle();

			// Redraw the document to update the view
			redrawDocumentView();
			redrawDocumentView();

			if (fileTypeExt == kAIPDFFileFormatExtension)
			{
				error = sAIActionManager->AIActionSetString(vpb, kAISaveDocumentAsFormatKey, kAIPDFFileFormat);
				check_ai_error(error);

				error = sAIActionManager->AIActionSetInteger(vpb, kAIPDFCompatibilityKey, AIPDFCompatibility::kAIPDFCompatibility17);
				check_ai_error(error);

				error = sAIActionManager->AIActionSetBoolean(vpb, kAIExportDocumentSaveMultipleArtboardsKey, false);
				check_ai_error(error);

				// Generate thumbnails.
				error = sAIActionManager->AIActionSetBoolean(vpb, kAIPDFGenerateThumbnailsKey, true);
				check_ai_error(error);

				error = sAIActionManager->AIActionSetBoolean(vpb, kAIPDFEmbedICCProfilesKey, true);
				check_ai_error(error);
			}

			// App Idle
			applicationIdle();

			// Redraw the document to update the view
			redrawDocumentView();
			redrawDocumentView();

			// Play action
			error = sAIActionManager->PlayActionEvent(kAISaveDocumentAsAction, kDialogOff, vpb);
			check_ai_error(error);

			error = sAIActionManager->AIActionSetBoolean(vpb, kAICloseAndSaveDocumentKey, false);
			check_ai_error(error);

			// Delete action
			error = sAIActionManager->AIDeleteActionParamValue(vpb);
			check_ai_error(error);

			// Acquire suite for use
			error = sSPBasic->AcquireSuite(kAIDocumentSuite, kAIDocumentSuiteVersion, (const void **)&sAIDocument);
			check_ai_error(error);

			// Do not show the save dialog
			AIDocumentHandle handle = currDoc->aiDocumentHandle();
			sAIDocument->GetDocument(&handle);
			error = sAIDocument->SetDocumentModified(false);

			// Close the document
			//currDoc->close();

			// Release Suite after use
			error = sSPBasic->ReleaseSuite(kAIDocumentSuite, kAIDocumentSuiteVersion);
		}
	}
	catch (ai::Error& ex)
	{
		error = ex;
		//TODO: Handle error
	}

	// Release Suites after use
	error = sSPBasic->ReleaseSuite(kAIActionManagerSuite, kAIActionManagerSuiteVersion);
}

And the code to handle AppIdle and Redraw.

void applicationIdle()
{
	ASErr error = kNoErr;

	// Acquire Suites needed
	error = sSPBasic->AcquireSuite(kAIUserSuite, kAIUserSuiteVersion, (const void **)&sAIUser);
	check_ai_error(error);

	error = sAIUser->AppIdle();
	check_ai_error(error);

	// Release Suite after use
	error = sSPBasic->ReleaseSuite(kAIUserSuite, kAIUserSuiteVersion);
}

void redrawDocumentView()
{
	ASErr error = kNoErr;

	// Acquire Suites needed
	error = sSPBasic->AcquireSuite(kAIDocumentSuite, kAIDocumentSuiteVersion, (const void **)&sAIDocument);
	check_ai_error(error);

	// Redraw the document to update the view
	error = sAIDocument->RedrawDocument();
	check_ai_error(error);

	// Release Suite after use
	error = sSPBasic->ReleaseSuite(kAIDocumentSuite, kAIDocumentSuiteVersion);
}

 

 

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Jul 17, 2022 Jul 17, 2022

Copy link to clipboard

Copied

Having a heck of a time getting past my issue here. I've been trying to implement the timer but cannot get it to fire. At least it is not hitting the GoTimer function as I was expecting it would. Don't know if it's my getting to know how to program Illustrator and C++, and something that I don't understand.

 

Here is my code to try and get the timer to work. As shown, AddTimer is being tested in my plugin's startup() but I am not getting it to hit the GoTimer function. Am I doing something wrong? I have added break points in both startup() and GoTimer(). The break point in startup() is being hit just fine and I can step through the code. The break point in GoTimer() is never hit.

 

ASErr clpcsx::Plugin::GoTimer(AITimerMessage* message)
{
	goTimer(message);

	return kNoErr;
}

void clpcsx::Plugin::startup()
{

	ASErr error = kNoErr;

	// Acquire Suites needed
	error = sSPBasic->AcquireSuite(kAITimerSuite, kAITimerSuiteVersion, (const void **)&sAITimer);
	check_ai_error(error);

	ai::int32 delay = 0;
	AITimerHandle timer = nullptr;

	clpcsx::Plugin *plugin_ = clpcsx::Plugin::instance();

	error = sAITimer->AddTimer((SPPluginRef)plugin_, "saveDocAsTimer", delay, &timer);
	check_ai_error(error);

	// Release Suite after use
	error = sSPBasic->ReleaseSuite(kAITimerSuite, kAITimerSuiteVersion);
}

 

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jul 17, 2022 Jul 17, 2022

Copy link to clipboard

Copied

Looking at my code, it appears I neglected an important pair of lines!

 

error = sAITimer->SetTimerActive(timer, true);
THROW_EXCEP_IF(error);

I suspect that will solve your problem! Sorry I missed it!

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Guide ,
Jul 17, 2022 Jul 17, 2022

Copy link to clipboard

Copied

LATEST

Oh, and when you receive the timer, you should probably disable it 🙂

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines