top of page
  • ZackW

FreeCAD Devlog 1 - Copy/Paste Into

Updated: Aug 16, 2022

WIP post! Like most others right now :)


So, I find myself doing a lot of programming and I find myself doing a lot of CAD work. My primary focus is typically electrical design (or at least that's what I would like it to be), but the three skills often go together. For CAD design, I'm often using a program called FreeCAD. And, since it's an open source project, it's an opportunity to train another one of the necessary skills, programming. FreeCAD is largely written in C++ and Python, languages I'm already familiar with. The problem is, I've become pretty lazy in C and C++, sticking to concepts I'm already familiar to solve all problems, and (oh the horror) I'm often using the Arduino environment which is pretty stripped of advanced C or C++. Working on FreeCAD is an opportunity to practice better C++ and adhere to stricter requirements. It's also neat that it's fully cross-platform and also is something that can make my life easier if I work on it.


Alright, so that's that. But about copy and paste?

Annoyingly, FreeCAD does not currently support copy and paste-into functionality. You can drag items in the tree under each other, to set a hierarchy, but you can't paste-into an existing group or body. This bothers me a lot, since my particular workflow often includes copying sketches and bodies into other bodies and parts.


Basic Changes Proposed

In the most basic sense, I want to be able to copy something, select a body or part, and when I hit paste (usually Ctrl-V), I want that thing I copied to be placed under the thing I selected. This functionality is great, but explodes instantly when you start thinking about other implications of an improved copy/paste system. First of all, there are a lot of different things in FreeCAD you can copy. There are draft, sketch, part, body... Tons of different things. The code is also structured in such a way that you can't (really) have a singular piece of code handling all of that. And there's no way I can implement all of that right away, anyway. So, I'm left with these basic requirements:

  1. Extensible

  2. Possible for a non expert like myself to write

  3. Fulfills basic paste-into functionality

Commands in FreeCAD

FreeCAD has a system of commands that controls just about everything you do in the FreeCAD GUI. This system actually seems to be a pretty good one, with each command sequestered in it's own module and reporting back to the core about itself. This also appears to be related to the system that keeps track of operations you can undo/redo. Anyway. This system of commands is already responsible for handling the copying and pasting of stuff in FreeCAD but that code (in src/Gui/CommandDoc.cpp) is part of the core and difficult to make work with specific circumstances or functions like paste-into. The basic paste command (StdCmdPaste) uses "mime" data to store the object, but I'm not entirely sure how that works. Once the data is converted to something that FreeCAD understands, out of the clipboard, it's just thrown into the document itself and not under any parent.


Really what I need is that commands could be overrided or overloaded or something. It doesn't seem to be infinitely complicated, but would definitely be a challenge to figure out to what extent that was possible. As is, the thing to do is to check around and see if there's any pre-existing examples of module re-implementing a command (like paste) that the core might have already implemented. There doesn't appear to be, and all commands seem to be prefaced with the module name. Example part commands are like, 'CmdPartDesignFillet'. So, the second best thing is probably just to implement paste (and possibly copy?) directly in each module. It shouldn't be too difficult to have a fallback command, which would just be the original one that exists right now.


So, the problem arising from all that is we need to be able to grab the same keyboard shortcut system that the original command uses. The keyboard shortcut itself seems not too difficult? I'm not sure how I would have it handle modules preferentially. The paste command sort of "header" declaration looks like this:


The function 'keySequenceToAccel' seems to tap into some fairly basic QT stuff, but it's not entirely clear to me what it's doing. The 'sAccel' tag doesn't seem to be present on a lot of other commands. This whole "how does this even get called" thing stumped me for a bit, so I decided to look into how commands even work in the first place.


At the very core of things, FreeCAD uses Qt's QAction class/system. I think the FreeCAD commands are a sort of more flexible way to extend the QAction functionality, there's a paragraph about that on the wiki somewhere I believe. Anyway. An Action seems to have a shortcut tied to it, which is a QKeySequence. If we hunt around for this in FreeCAD, we find something similar to this written a few times:

Remember that sAccel thing earlier? There it is. I don't know what's up with the whole 'fromLatin1' thing, but I think it's pretty obvious that the sAccel tag set when defining a command is what is responsible for setting the shortcut. Don't ask me how the menu icons work, those aren't the next most important step. At this point, it's my belief that commands seem to be called typically either as a command (python scripts seem to be responsible for calling a lot of them), or can be activated from a keyboard shortcut registered with QT.


Backing up a bit here, we can think a bit about how this implementation might work. We know that we most likely assign keyboard shortcuts to our commands, but we don't know what happens when we have many different commands using the same shortcut. We know that at least at first, we can just put the paste code in a PartDesign and/or part command, but that paradigm would imply we should also make specific commands for all the other modules. A third option starts to seem more attractive, which is to potentially have our own module just dedicated to working with copy and pasting. That should mean we don't have to modify core behavior too much, and that we can extend the module over time, but it might get kinda hairy with all of the other modules our "CopyPaste" (clipboard?) module has to work with. There's also the small issue of module creation, and how hard that might be.


Well how hard are modules to write? Let's take a quick peek. There's actually a wiki article on this, which goes over the basics. This seems to include a module init file, fair, and can be for both either App or Gui; we'd be writing a pure Gui module in all likelihood. That article (annoyingly?) also covers a bit about commands, but doesn't seem to add a whole lot more on the subject. Regardless, this seems to be the simplest start of a module:


Okay so at this point the obvious thing to do is to go and see if there are some examples of small modules I can learn from. I'd classify the spreadsheet modules as probably a pretty small one, so I'm looking there first. This is the InitGui.py file:

Pretty short, right? There's also the 'Complete' module, which seems to be an example for people like me. That one literally has an comment-only Init.py file, and a InitGui.py file that looks like this:

Alright so at this point I'm thinking it's probably not the hardest thing to just make our own module for copy and paste. I'm also seeing other modules include other modules, something that I'm sure would be a very bad idea for core modifications. Alright so, let's do it! Let's make our own module. I'm just going to make a copy of the 'Complete' module and re-name it, commenting out the stuff I'm not using.

I went ahead and just named it Clipboard for now, we'll see what other members of the community think about this approach and the names later. Anyway, I made that module and I hit compile, and watched the pretty colors start to go by on my system monitor.


Okay and of course that compilation failed since I left some random characters in one of the files on accident. So, feed it a few more threads and run the compilation again, and wait. Sweat while you watch your system runs out of RAM. Then, curse internally when it does run out of RAM and firefox crashes. Maybe 200 tabs are too many? At least the compilation finished successfully.

Okay sweet, so, we probably have our very own module! Well sorta, I think at this point it's doing literally nothing since it's not announcing itself in any way. I'm going to sketch out some potential commands in a Command.cpp file under our new Gui folder, and go from there. We'll probably need to make our own namespace and all that kind of stuff, but for now I just want to take baby steps towards our goal.


Dependency Selector


One of the things I should fix while working on this, is the way the copy command opens a dialog to select object dependencies. I think this was added a couple versions ago, and has some interesting effects. In general, it should be smarter when copying basic stuff like sketches. It really shouldn't be required to ask whether or not you want to create a copy of the base origin when you copy a sketch, in my opinion. In other scenarios it causes issues too, such as creating duplicate spreadsheets when it naively copies them as a dependency. This little dialog seems to be triggered via a "msg";

Pretty difficult call to reverse engineer, but not impossible. The sendMsgToFocusView function seems difficult to track, and I don't see anything handling anything relevant for the "Copy" message that's evidentially being sent... somewhere. At least, it's not apparently going anywhere in the c++ files. I went and did a wider search through the files just for the string title of the window that pops up ("Object selection"). That led to a .ui file in src/Gui, which references Gui::DlgObjectSelection. There appears to be a C++ file associated with that, great! It appears to make a QDialog and doesn't seem too terribly complicated, but there's the issue that this is also in the "Core" of FreeCAD. This should probably be a smart selector, with access to knowing things like what kind of object is currently selected. I'm going to post on the forum at this point with this stuff, and probably start working on a second devlog rather than continuing to stretch this one. If we have to eventually wrap up the dependency selector with the clipboard code, perhaps calling the module "Clipboard" is not the best idea.

70 views0 comments

Recent Posts

See All
bottom of page