Subscribe to this thread
Home - General / All posts - drawing.paste method not working
steveFitz

155 post(s)
#18-Mar-16 03:39

I have a script for flattening layers imported from CAD onto a single drawing that I use quite often.

Recently it stopped working with an error occurring on the drawing.paste line.

I tried a few other script examples from the forum and they seem to bug on the same .paste line.

Is it me? Have I forgotten something fundamental?!

I have tried on 2 different computers using Windows 7 32 and 64 bit Manifold 8.0.29 (32 and 64 bit also).

I have tried with different CAD imports but still no go.

For a simple example try this script.

Can some one please confirm that this is/isn't happening?

Could it be a Windows update thing?

Steve

tjhb

7,452 post(s)
#18-Mar-16 05:02

Let's see.

I have a script for flattening layers imported from CAD onto a single drawing that I use quite often.

What is the script?

Recently it stopped working with an error occurring on the drawing.paste line.

What was the error?

I tried a few other script examples from the forum and they seem to bug on the same .paste line.

You give one example script by reference, but no example of how you are using it, no data, and no hint of the error.

Can some one please confirm that this is/isn't happening?

No one can since there are no details.

Could it be a Windows update thing?

Definitely. Or sunspots.

steveFitz

155 post(s)
#18-Mar-16 06:09

tjhb,

Thanks for taking the time (even if your tone is a tad condescending).

What is the script?

The actual script is not important. What is important is that every/any script I try that has copy/past in vbscript bugs out at the .past line.

What was the error?

"Error at Line 19" (in the script example below it will be error at line 7)

You give one example script by reference, but no example of how you are using it, no data, and no hint of the error.

My example is in the link (you may have missed the "this" link underlined). I linked to this simple example because reduces the problem to the simplest form with no other complicating factors. My own script is long and may only cause confusion ("why do you do this here... why don't you do that....") or be not well enough formed for public viewing. It has worked for a few years though.

The script in the link is from firstube and is:

'vbscript

Sub Main

 set comps = document.componentset

 set newdwg = comps("Drawing")

 for each comp in comps

 if comp.Type = componentdrawing and comp.Name <> newdwg.Name then

            comp.Copy

            newdwg.Paste

 end if

 next

End Sub

No one can since there are no details.

I could have been more clear. Apologies. It is the script above I would like confirmation on.

If you have a few drawings and a blank drawing called "Drawing" the script should past their drawing objects into "Drawing".

Definitely. Or sunspots.

Never underestimate sunspot activity or full moon effects.

Steve

steveFitz

155 post(s)
#18-Mar-16 10:12

Interesting....

I just tried the exact same scripts with the same data on an old laptop with Windows Vista and the scripts work as expected - the .paste method does not error.

Maybe sunspot activity?

dale

520 post(s)
#18-Mar-16 10:59

Fitzy,

I closed the blinds and curtains, to rule out sunspots, full moons, and other atmospheric variations.

Script linked and posted above works as expected.

Windows 7 64bit (Running on OS X 10.10.5 Parallels) Manifold 8.0.29. Tested both 32bit and 64bit

steveFitz

155 post(s)
#18-Mar-16 11:05

Dale,

Thanks very much for testing and reporting.

I'll keep investigating

cheers

firsttube


1,427 post(s)
#18-Mar-16 12:55

I've had issues with the .paste method before too, and exactly as you describe sometimes it errors and sometimes not. I actually think it has more to do with the Windows clipboard than Manifold. There are ways to do the "paste" other than using the .paste method. For example, you could work with the object set instead of using the clipboard via the paste method.

This script will run much slower because it looks at each object and each column and compares the columns in the source and target drawings. If the data in the table is not important you could remove that part.

'vbscript

Sub Main

    set comps = document.componentset

 'objects of source drawing that you want to copy

    set objsSource = comps("Drawing").objectset

 'objects in source drawing that you want to paste to

    set objsTarget = comps("Drawing 2").objectset

 'source drawing table columns

    set colsSource = comps("Drawing").OwnedTable.ColumnSet

 'target drawing table columns

    set colsTarget = comps("Drawing 2").OwnedTable.ColumnSet

    for each obj in objsSource

    set g = obj.Geom

    objsTarget.Add(g)

    set objLast = objsTarget.LastAdded

    for each colTarget in colsTarget

    for each colSource in colsSource

    if colTarget.Name = colSource.Name _

    and colTarget.Type = colSource.Type _

    and colTarget.Name <> "ID" _

    and colTarget.IsIntrinsic = False then

    objLast.Record.Data(colTarget) = obj.Record.Data(colSource)

 end if

 next

 next

 next

 

End Sub


"The blessing in life is finding the torture you are comfortable with." - Jerry Seinfeld, 6/26/2013

tjhb

7,452 post(s)
#18-Mar-16 14:16

I think firsttube is right that problems with .Paste are more due to Windows clipboard than to Manifold.

It makes intuitive sense to allow time for the .Copy operation to finish, before issuing the dependent .Paste. I've only found that essential in UI scripting under a separate thread (specifically when pasteing drawings as surfaces), but perhaps it can apply (intermittently) under the main thread too.

A delay after each .Copy might help.

But scripted SQL would be the fastest way to accomplish the goal here.

Is combining all drawings into one the actual goal here, or was it just an example?

firsttube


1,427 post(s)
#18-Mar-16 14:19

I agree, absolutely SQL would be the best option here. I thought the OP wanted a script, but SQL is the way to go.


"The blessing in life is finding the torture you are comfortable with." - Jerry Seinfeld, 6/26/2013

steveFitz

155 post(s)
#20-Mar-16 22:28

Thanks for that firstube.

I found some other references to problem with .copy/.paste on the forum, e.g:

http://www.georeference.org/forum/t31108.18

but they seemed to be concerned with IMS and external apps.

There is also some info on the web discussing other apps and windows updates possibly interfering with clipboard functions.

The interesting thing is that my script stopped working suddenly after using for at least a couple of years. I doesn't work at all now and I don't ever remember having intermittent issues with it.

It does appear to work if you give it something to do between copy and paste - e.g. Application.History.Log ".".

As a matter of interest and to try to nail down if copy/paste is pretty much useless in scripts for now, can you tell me if the short script of yours above works for you?

Steve

tjhb

7,452 post(s)
#21-Mar-16 00:04

The interesting thing is that my script stopped working suddenly after using for at least a couple of years. I doesn't work at all now and I don't ever remember having intermittent issues with it.

It does appear to work if you give it something to do between copy and paste - e.g. Application.History.Log ".".

There is a good reason for this, which hopefully I've explained.

It's only a bad scripting style that "causes" the issue. "Bad" is a bit strong I know--"naive" or "thoughtless" is probably better (but I don't like their tone).

This is not a Manifold issue.

As a matter of interest and to try to nail down if copy/paste is pretty much useless in scripts for now...

Copy and paste is perfectly useful. The problem is or was with your script. (Many of mine in the past too, and many headaches trying to understand.)

Sure, Manifold could have made it easier for us to script this stuff without having to think so hard. They could have built in delays, or checks, which would have made it always OK to issue a .Paste command directly after .Copy. (At least I think they could. It might be more tricky with some scripting languages than others.)

But remember, when they coded this, affordable computers were not nearly so fast. The faster the hardware, the more important, the more subtle and more bothersome timing issues become. This is a problem we ought to be happy to have.

But we do need to think!

There is nothing inherent about issuing commands A then B that guarantees that A will complete before B starts. One way or another, someone needs to actually check that. If not Manifold internally, or the scripting language itself, then us.

Being careful is not so hard. It's also good practice for future versions.

tjhb

7,452 post(s)
#21-Mar-16 00:47

There is nothing inherent about issuing commands A then B that guarantees that A will complete before B starts.

This is true as well (more surprising and probably rare):

There is nothing inherent about issuing commands A then B that guarantees that A will start before B starts.

Step A may have substantial setup conditions, while step B has almost none. In that case, unless someone actually checks (the language, the context, or us), B gets to begin before A has "got its trousers on".

I have struck that using UI scripting via VBScript in Manifold 8 (only in one case, but reproducibly).

We can't just assume linear dependence. We either need to check, or rely on luck.

tjhb

7,452 post(s)
#21-Mar-16 06:01

.

oisink
360 post(s)
#18-Mar-16 18:00

Steve

We obviously do the same things. Try the attached script. Some explanation:

I have a folder named "DWG" with a sub-folder named "DWG_Import". I always import my .dwg files into "DWG_Import", and a Map component with all the DWG layers is automatically created in that folder.

In the DWG folder I have two components: (1) a Drawing "Topo"; and (2) a Drawing "Topo text". (This is so that I can export labels to .shp format).

I then run the script below. (Note, it Assigns BNG projection from an XML file to ALL components each time it runs. Strictly, it need only assign projection to the newly imported DWGs, but since I don't have too many components and I've had problems with mixed projections, I'm happy to ensure all components are BNG each time).

The script enumerates the layers of the Map component in the DWG_Import folder - it only looks for one MAP, and last one wins - a good reason to import the .dwg file into a separate folder. For all visible layers, it copies the objects to the target drawings. I generate and run SQL for Drawing Components but use entirely script for Label Components - I can't work out how to use SQL with the labels, which would be faster.

I hope this is of assistance.

Attachments:
FlattenDWG.txt

tjhb

7,452 post(s)
#18-Mar-16 21:23

Regarding copying labels, you can maybe do this a little bit faster via SQL, but you'd have to make one query per label, since we don't have access to a table for (unbound) labels.

You'd use Label.Geom.Center.X and .Y, feeding these values to NewPoint, wrapped in AssignCoordSys() for the target.

I haven't tested it but I doubt it would be significantly faster than going fully via the object model (exactly as you do).

On the other hand, we could copy and paste unbound labels components as drawings or tables first (with perhaps a short delay between .Copy and .Paste...), then use SQL without going via the object model.

oisink
360 post(s)
#18-Mar-16 21:34

Hi Tim

I thought of copying and pasting the label component as a drawing, but couldn't work out how to do it. Any clues and I'll give it a try

Oisin

tjhb

7,452 post(s)
#18-Mar-16 22:20

We have to use UI scripting.

The delay here spins a core uselessly--we're stuck with that in VBScript.

If you pass the function a folder name it only looks there. Use an empty string for all labels in the project.

' VBScript

Sub Main

    drawings = LabelsToDrawings("")

    Application.History.Log CStr(drawings) & " component(s) pasted as drawings." & vbCrLf, True

End Sub

'

' *************************************************************************************

Private Function LabelsToDrawings(folder_name)

    ' Run in a separate thread

    Dim ui

    Dim source ' string

    Dim comps, comp ' ComponentSet, Component

    Dim copied ' int

    '

    Set ui = Application.UserInterface

    Set comps = Document.ComponentSet

    copied = 0

    '

    source = "project" ' by default, search whole project

    If folder_name <> "" Then

        If Document.ComponentSet.ItemByName(folder_name) >= 0 Then

            ' named component exists...

            With Document.ComponentSet(folder_name)

                If .Type = ComponentFolder Then

                    ' ... and is a folder

                    ' only search this folder

                    Set comps = .Children

                    source = "[" & .Name & "]"

                End If

            End With

        End If

    End If

    Application.History.Log "Copy and paste labels components in " & source & " as drawings..." & vbCrLf, True

    '

    For Each comp In comps

        If comp.Type = ComponentLabels Then

            ' Select the labels component in the Project pane

            Application.History.Log "[" & comp.Name & "]" & vbCrLf, True

            ui.Panes("Project").ControlSet("TreeViewComponents").Text = comp.Name

 

            ' Copy to the clipboard

            Application.History.Log vbTab & "Copy"True

            ui.InvokeCommand "ViewProjectCopy""pane: Project"

            Delay 3, True

            ' Invoke PasteAsDrawing dialog (no options for labels)

            Application.History.Log vbTab & "Paste as drawing" & vbCrLf, True

            ui.InvokeCommand "ViewProjectPasteAsDrawing""pane: Project"

            copied = copied + 1

        End If

    Next ' comp

    '

    LabelsToDrawings = copied

End Function

'

'

' *************************************************************************************

Private Sub Delay(s, doLog)

    ' The Shell.Sleep method is not available within the Manifold context

    ' so we use an "active" loop instead (occupies a core)

    Dim i

    Dim start

    '

    For i = 1 To s

        start = Now

        Do ' Nothing

        Loop Until DateDiff("s", start, Now) >= 1

            ' More robust than Timer function

            ' if operation spans midnight

        If doLog Then Application.History.Log "."True

    Next

    If doLog Then Application.History.Log vbCrLf, True

End Sub

[Made a quick fix after testing.]

Attachments:
Copy and paste labels as drawings.txt

tjhb

7,452 post(s)
#18-Mar-16 23:50

Same thing in IronPython.

Advantage: Python's built-in time.sleep() function does not uselessly spin a core.

# IronPython

import clr

clr.AddReference('Manifold.Interop')

from Manifold.Interop import ComponentType # enum (else use .TypeName)

import time

log = Application.History.Log

#

def Main():

    drawings = labels_to_drawings('')

    if drawings == 0:

        log('(No labels components found.)\r\n', True)

    elif drawings == 1:

        log('1 component pasted as a drawing.\r\n', True)

    else:

        log(str(drawings) + ' components pasted as drawings.\r\n', True)

#

# *************************************************************************************

def labels_to_drawings(folder_name):

    # run in a separate thread

    # optionally pass in a folder name;

    # default is all labels components in the project

    #

    ui = Application.UserInterface

    comps = Document.ComponentSet

    source = 'project' # by default, search whole project

    copied = 0

    #

    if folder_name != '':

        if comps.ItemByName(folder_name) >= 0:

            # named component exists...

            comp = comps(folder_name)

            if comp.Type == int(ComponentType.ComponentFolder):

                # ... and is a folder

                # only search this folder

                comps = comp.Children

                source = '[' + comp.Name + ']'

    log('Copy and paste labels components in ' + source + ' as drawings...\r\n', True)

    #

    for comp in comps:

        if comp.Type == int(ComponentType.ComponentLabels):

            # select the labels component in the Project pane

            log('[' + comp.Name + ']\r\n')

            ui.Panes('Project').ControlSet('TreeViewComponents').Text = comp.Name

            #

            # copy to the clipboard

            log('\tCopy')

            ui.InvokeCommand('ViewProjectCopy', 'pane: Project')

            delay(3, True)

            #

            # invoke PasteAsDrawing (no options for labels)

            log('\tPaste as drawing\r\n')

            ui.InvokeCommand('ViewProjectPasteAsDrawing', 'pane: Project')

            copied += 1

    return copied

#

# *************************************************************************************

def delay(s, doLog):

    for i in xrange(1, s + 1):

        time.sleep(1)

        if doLog: log('.')

    if doLog: log('\r\n')

#

# *************************************************************************************

#

Main()

Attachments:
Copy and paste labels as drawings (ipy).txt

oisink
360 post(s)
#19-Mar-16 17:08

Did you speed test the versions against each other?

tjhb

7,452 post(s)
#19-Mar-16 18:53

No.

If you test yourself, please report.

First though I suggest adjusting the delay after each .Copy from 3s to something realistic for your typical or actual number of labels. 3s is too long--almost certainly most of the processing time is this waiting.

That said, I wouldn't expect language to make any real difference to execution time here since there is so little CPU work being done (except for the delay loop in the VBScript version--but that is fixed time).

steveFitz

155 post(s)
#20-Mar-16 22:34

Oisin,

Yes, we are pretty much doing similar work it would seem, at least with regard to the data side.

I'll try to link up with you on Linked-in so we can have further discussion about our work flows and all think tree related without boring the forum (if that's OK with you of course).

Thanks for the script. Apart from actually working, it is better than mine as it retains the layer and colour names in the drawing table.

I'll augment it to automate the creation of the import folder and drawings and perhaps assign projections too.

I don't usually bother with the labels out in the field unless they are existing tree ID numbers.

As CAD drawings often have many labels and exporting to shape file will create separate ones for lines, areas and points it could get laborious turning on and off the ones you need to see in the field (?).

Another feature I'd like with the script would be to just replicate the area of interest. CAD often has page layout, legend, etc. objects in space outside the area of interest that only serve to make zoom all functions unhelpful. I sometimes think I'm going to run into a giant North Arrow when I'm out following my field CAD with my GNSS!

I'll post a moded script when done and when the dust settles on the forum (to make sure all contributions are accounted for).

Steve

[Edited - some additional comments added]

tjhb

7,452 post(s)
#21-Mar-16 06:21

I'll post a moded script when done and when the dust settles on the forum (to make sure all contributions are accounted for).

This post and the sentiment behind it are in my opinion outside the spirit of the forum.

We are not here just to [complain and] take.

tjhb

7,452 post(s)
#21-Mar-16 07:22

Actually Steve might have meant that in quite a different way from how I read it.

Please ignore my comment.

tjhb

7,452 post(s)
#21-Mar-16 07:51

If anyone wants to address the problem originally raised in this thread, I suggest they start a new one.

There's lots to discuss.

oisink
360 post(s)
#21-Mar-16 09:00

Hi Steve.

Happy to join up on LinkedIn - just sent you an invite.

Another feature I'd like with the script would be to just replicate the area of interest.

That's why I enumerate the map.layers and only flatten visible ones - I turn off those layers with viewports, title blocks, legends etc.

Regards

Oisin

oisink
360 post(s)
#21-Mar-16 13:01

But AOI could be implemented via the SQL part of my script by, say, adding a new drawing, [AOI Drawing], to the map as a layer, drawing your AOI as an area and modifying the SQL to only insert objects that touched objects in [AOI Drawing]. (You'd need to turn off the visibility of [AOI Drawing], or specifically exclude it from the script, else the AOI objects will get flattened too.

tjhb

7,452 post(s)
#18-Mar-16 05:15

It's likely that you're doing something wrong, misunderstanding something, or that you have data that for some reason is uncooperative.

Post some data, post some code, ask an open question.

We're all wrong--it's only a matter of now or later and it doesn't matter.

tjhb

7,452 post(s)
#20-Mar-16 01:58

I made a lot of progress today on manageing what Manifold is doing with the clipboard, via .NET.

In particular, before issuing .Copy, how to clear the clipboard; then after .Copy, how to check for data of the expected Manifold type before issuing .Paste, using the format names Manifold uses to store data on the clipboard.

This should enable us to have fine control over Copy and Paste timing, without introducing arbitrary delays.

tjhb

7,452 post(s)
#21-Mar-16 21:54

See this thread.

Manifold User Community Use Agreement Copyright (C) 2007-2017 Manifold Software Limited. All rights reserved.