Jump to content

twk

Member
  • Posts

    941
  • Joined

  • Last visited

Reputation

525 Spectacular

Personal Information

  • Occupation
    Architectural Designer
  • Homepage
    www.cba-design.co.nz
  • Location
    New Zealand

Recent Profile Visitors

8,099 profile views
  1. Condolences to the family. Never met the legend face to face, but the countless back and forths on the forum, both publicly and privatley, made us very well acquainted. Thanks @Pat Stanford for letting us know. He will be dearly missed.
  2. Anyone experienced this before? First time I've seen. Any soutions?
  3. Better yet, a custom class to encapsulate the process: (Default disclaimers apply, "Save first, save often, use at your own risk. Any lost work will be between you and the machine") import winreg from typing import Dict, Any, Optional, Union, Tuple class VW_WinReg: def __init__(self, parent_key: str = r"Software\Nemetschek\Vectorworks 30", hive: int = winreg.HKEY_CURRENT_USER): """ Initialize the VW_WinReg class to access Vectorworks registry data. Args: parent_key: The starting registry key path (default: "Software\\Nemetschek\\Vectorworks 30") hive: The registry hive to use (default: HKEY_CURRENT_USER) """ self.parent_key = parent_key self.hive = hive self.registry_data = {} self._load_registry_data() def _load_registry_data(self) -> None: """Load all registry data from the parent key.""" try: self.registry_data = self._get_key_data(self.parent_key) except WindowsError as e: self.registry_data = {"error": f"Failed to access registry key: {e}"} def _get_key_data(self, key_path: str) -> Dict[str, Any]: """ Recursively get all data from a registry key and its subkeys. Args: key_path: The registry key path to read Returns: A dictionary containing all values and subkeys """ result = {"_values": {}, "_subkeys": {}} try: with winreg.OpenKey(self.hive, key_path) as key: # Get values try: i = 0 while True: try: name, data, data_type = winreg.EnumValue(key, i) # Process data based on type if data_type == winreg.REG_BINARY: processed_data = self._process_binary_data(data) elif data_type == winreg.REG_DWORD: processed_data = int(data) elif data_type == winreg.REG_SZ or data_type == winreg.REG_EXPAND_SZ: processed_data = str(data) elif data_type == winreg.REG_MULTI_SZ: processed_data = list(data) else: processed_data = data # Get type name type_name = self._get_type_name(data_type) # Store in dictionary result["_values"][name] = { "value": processed_data, "type": type_name } i += 1 except WindowsError: break # No more values except WindowsError: pass # No values in this key # Get subkeys try: i = 0 while True: try: subkey_name = winreg.EnumKey(key, i) subkey_path = f"{key_path}\\{subkey_name}" # Recursively get subkey data result["_subkeys"][subkey_name] = self._get_key_data(subkey_path) i += 1 except WindowsError: break # No more subkeys except WindowsError: pass # No subkeys except WindowsError as e: result["_error"] = str(e) return result def _process_binary_data(self, data: bytes) -> str: """Process binary data to make it more readable.""" try: # Try to decode as UTF-16 return data.decode('utf-16-le').rstrip('\x00') except: # Fall back to hex representation return data.hex() def _get_type_name(self, data_type: int) -> str: """Convert registry type code to a readable name.""" type_names = { winreg.REG_SZ: "REG_SZ", winreg.REG_MULTI_SZ: "REG_MULTI_SZ", winreg.REG_DWORD: "REG_DWORD", winreg.REG_QWORD: "REG_QWORD", winreg.REG_BINARY: "REG_BINARY", winreg.REG_EXPAND_SZ: "REG_EXPAND_SZ" } return type_names.get(data_type, f"Type_{data_type}") def get_all_data(self) -> Dict[str, Any]: """Get all registry data as a dictionary.""" return self.registry_data def get_value(self, path: str) -> Optional[Any]: """ Get a specific registry value using a path notation. Args: path: Path to the value (e.g., "General/Workgroup Folder 0") Returns: The value if found, None otherwise """ parts = path.split('/') current = self.registry_data for i, part in enumerate(parts): if i == len(parts) - 1: # Last part (value name) if "_values" in current and part in current["_values"]: return current["_values"][part]["value"] return None else: # Subkey if "_subkeys" in current and part in current["_subkeys"]: current = current["_subkeys"][part] else: return None def get_workgroup_folders(self) -> Dict[str, str]: """ Get all Workgroup Folders from the registry. Returns: Dictionary of Workgroup Folder names and paths """ folders = {} # Check if General subkey exists if "_subkeys" in self.registry_data and "General" in self.registry_data["_subkeys"]: general = self.registry_data["_subkeys"]["General"] if "_values" in general: # Find all Workgroup Folder entries for name, data in general["_values"].items(): if name.startswith("Workgroup Folder "): folders[name] = data["value"] return folders def print_registry_tree(self, start_dict=None, indent="", path=""): """ Print the registry data as a tree. Args: start_dict: Dictionary to start from (default: full registry data) indent: Current indentation level path: Current path in the registry """ if start_dict is None: start_dict = self.registry_data print(f"{indent}[{self.parent_key}]") indent = " " # Print values if "_values" in start_dict: for name, data in start_dict["_values"].items(): print(f"{indent}{name} = {data['value']} ({data['type']})") # Print subkeys if "_subkeys" in start_dict: for subkey_name, subkey_data in start_dict["_subkeys"].items(): full_path = f"{path}\\{subkey_name}" if path else subkey_name print(f"{indent}[{full_path}]") self.print_registry_tree(subkey_data, indent + " ", full_path) The VW_WinReg class provides a structured way to access and navigate Vectorworks registry data on Windows: Recursively reads the Windows registry starting from a specified Vectorworks key path : defaults to ("Software\Nemetschek\Vectorworks 30") change to suit Organizes all registry data into a nested dictionary structure : get_all_data() Properly handles different data types (strings, numbers, binary data) Provides methods to: Get all registry data as a dictionary Access specific values using path notation Extract Workgroup Folders settings Print the registry tree in a readable format This makes it much easier to programmatically access Vectorworks settings stored in the registry compared to using the raw winreg module directly. The class handles all the complexity of registry access, error handling, and data organization. Example code use: Getting and displaying the full registry data structure Extracting and displaying all Workgroup Folders Printing the registry tree in a readable format Retrieving specific values using path notation The output will show the Vectorworks registry information in different formats, making it easy to access the data you need for your specific use case. import json # Create the VW_WinReg instance vw_reg = VW_WinReg() # Example 1: Get all registry data and print as JSON print("\n=== Example 1: Get All Registry Data ===") all_data = vw_reg.get_all_data() # Print first few entries of the data (full data would be too large) print(json.dumps(all_data, indent=2)[:500] + "...\n") # Example 2: Get Workgroup Folders print("\n=== Example 2: Get Workgroup Folders ===") folders = vw_reg.get_workgroup_folders() print("Workgroup Folders:") for name, path in folders.items(): print(f" {name}: {path}") # Example 3: Print Registry Tree print("\n=== Example 3: Print Registry Tree ===") print("Vectorworks Registry Structure:") vw_reg.print_registry_tree() # Example 4: Get specific values using paths print("\n=== Example 4: Get Specific Values ===") install_dir = vw_reg.get_value("InstallDir") user_folder = vw_reg.get_value("General/User Folder") workgroup_folder = vw_reg.get_value("General/Workgroup Folder 0") print(f"Install Directory: {install_dir}") print(f"User Folder: {user_folder}") print(f"Primary Workgroup Folder: {workgroup_folder}")
  4. needed this exact thing, thanks for pointing me in the right direction @DomC. For windows reg, python script below to scan through all Nemetschek keys in registry: import winreg import sys def list_registry_keys_and_values(key_path, hive=winreg.HKEY_CURRENT_USER, indent=""): """List all subkeys and values under a specific registry path.""" try: with winreg.OpenKey(hive, key_path) as key: # Print this key print(f"{indent}[{key_path}]") # List all values in this key try: i = 0 while True: try: name, data, data_type = winreg.EnumValue(key, i) # Convert binary data to string if possible if isinstance(data, bytes): try: data = data.decode('utf-16-le').rstrip('\x00') except: data = str(data) # Format the output type_names = { winreg.REG_SZ: "REG_SZ", winreg.REG_MULTI_SZ: "REG_MULTI_SZ", winreg.REG_DWORD: "REG_DWORD", winreg.REG_QWORD: "REG_QWORD", winreg.REG_BINARY: "REG_BINARY", winreg.REG_EXPAND_SZ: "REG_EXPAND_SZ" } type_name = type_names.get(data_type, f"Type: {data_type}") print(f"{indent} {name} = {data} ({type_name})") i += 1 except WindowsError: break # No more values except WindowsError: pass # Recursively list all subkeys try: i = 0 while True: try: subkey_name = winreg.EnumKey(key, i) subkey_path = f"{key_path}\\{subkey_name}" # Recursively list the subkey list_registry_keys_and_values(subkey_path, hive, indent + " ") i += 1 except WindowsError: break # No more subkeys except WindowsError: pass except WindowsError as e: print(f"{indent}Error opening {key_path}: {e}") def main(): # The registry path to explore registry_path = r"Software\Nemetschek" print(f"Listing all keys and values under HKEY_CURRENT_USER\\{registry_path}:\n") # List all keys and values list_registry_keys_and_values(registry_path) print("\nDone.") if __name__ == "__main__": main() This script will: Start at the Software\Nemetschek\ key in HKEY_CURRENT_USER List all values directly under this key Recursively list all subkeys and their values Format the output in a hierarchical structure with indentation Show the data type of each value The output will show you all the Nemetschek-related settings in your registry, including the Vectorworks settings you found earlier. Once you get the key value for your particular workgroup folder, you can use that
  5. Apologies, ignore previous post, this works in Vectorworks 2025, Update 4
  6. We are seeing this as well, this is a major hinderance to our workflow, @Benson Shaw, what was the vb bug number? is this getting addressed any time soon @Katarina Ollikainen
  7. Anyone else facing similar issues, so I can open a bug report? I wonder if project sharing server might work
  8. Icons for Plugins PNGs Need 2 versions, standard and Retina version, and for each a light and dark version (total of 4 files) Normal 26x20 - tool.png // Dark Version = tool_dark.png Doubled 54x40 - tool@2x.png // Dark Version = tool_dark@2x.png Must be 72dpi Icon size must be 26x20 pixels SVGs Need 2 versions, light and dark (total of 2 files only) Normal 26x20 - tool.svg // Dark Version = tool_dark.svg Must be 72dpi Icon size must be 26x20 pixels Online PNG to SVG Converter: https://svgconverter.app/free Online SVG Resize: https://www.iloveimg.com/resize-image 1. Develop or design icon in Vectorworks, to a 26x20 region. (I set a rectangle at this dimension, and set line weight to <None>, and have loci’s at top left and bottom right for easy snapping during export (where you’ll have draw a marquee around region for export). 2. Export png @72dpi. pixel dimensions can be whatever, but higher dimensions are better for svg conversion 3. Upload image to https://svgconverter.app/free. Vectorize, and download 4. Go to https://www.iloveimg.com/resize-image, upload, and resize to 26x20 5. Resize Image then download on next page: 6. You now have your svg file sized to the correct 26x20 dimensions, import now into your plugin via the Vectorworks plugin manager.
  9. Ok, so the solution is to also have a dark version (if you're on windows i believe): So all these must be fullfilled: - icon.png (72 dpi) 26x20(pixel wxh) - icon_dark.png (72 dpi) 26x20(pixel wxh) - icon@2x.png (72 dpi) 52x40(pixel wxh) - icon_dark@2x.png (72 dpi) 52x40(pixel wxh) Works now
  10. This is not working in 2024, when selecting an icon for a custom plugin: Workflow tested: - icon.png (72 dpi) 26x20(pixel wxh) - icon@2x.png (72 dpi) 52x40(pixel wxh) Anyone successfully assigned icons to a custom plugin/tool in Vectorworks 2024?
  11. the dailogHandler should have an init check portion. which is a constant stored in SetupDialogC (SetupDialogC = 12255) if item == SetupDialogC: # do initialisation stuff here pass Search forum for this.
  12. my code for checking if a named object exists is below: test_name = vs.StrDialog("Test Name: ", "") if vs.GetObject(test_name) not in [0, None, vs.Handle(0)]: # do stuff here pass
  13. You can do this with class-overrides, per viewport. You can't reassign an obeject's class via viewport, but you can override the graphic attributes of the class via viewport. For your case, just set the glass's class to solid white in the class-settings of the viewport
  14. Yes, we've had issues with project sharing for this. Sometimes user's work would not get committed. Even though they said they did. We also had an aging network cabling system in the office that also needed replacing. So it was hard to determine what was causing the fail-to-commit of these files, as it could be our network, it could be the project sharing implementation. The other headache being, trying to help people understand the concept of a working file, versus a project sharing (master) file, versus a normal vectorworks file. (vwxw, vwxp, vwx). So that route would introduce another new concept (read headache) to get across mindsets. If they are used to 2D symbols etc, this workflow can still be leveraged by having these 2D symbols referenced in, from a vwx symbol file. 'Simple' is a very relative term. It all depends. Sounds like the old guard need to be 'shown' the new workflow. I understand and emphathize with those who have for years have something work trying to understand why things need to change. So you really have to be robust with the reasonings for your changes. For me, 9 times out of 10 the reason would've be the software changes. Somehow I've become accustomed to the saying "the only constant is change". now embracing new things, tried it, broke it, fixed it. rinse, repeat. But I've also had to be very careful implementing change doesn't affect the timeframes, etc.
  15. As others have suggested, this is more or less a HR issue. But your plan is a good start, and take on others' advice on here as well. Also don't change for the sake of changing, if something is working for the timeframes and bottom line, leave it be. Fix and upgrade workflows on a have-to basis. And most of these would also be due to software changes (think the new Data-vis, Viewport Styles, etc features) I have been in a similar position, where implementing new workflows is never applauded. And understandably, you're (we are) basically disrupting existing workflows. What I had done was to take on a project solely, and implement my workflows to show that my way's are faster, more efficient and scalable and transferable. (The last two being the more important aspects). Purge unused classes, hatches, line styles (any and all resource types). Document class structures, resource structures, etc.
×
×
  • Create New...