Exploring the pencil2d F6

So one of thing I’m starting to  taking a look at this: https://github.com/pencil2d/pencil/issues/414

Playing around with the app, it looks like the F6 duplicate frame in combination with a pencil draw, F6 is yielded correct results.  (I’m thinking it might be worth understanding what’s happening on something that’s working correctly, for when I deal with the stuff that’s broke.
So… I wanted follow the trail on what’s going on with the F6 key..  I was thinking it was going to be sometype of keypress event, but I think I found the link here:

[shortcuts]
CmdNewFile=Ctrl+N
CmdOpenFile=Ctrl+O
CmdSaveFile=Ctrl+S
CmdSaveAs=Ctrl+Shift+S
CmdPrint=Ctrl+P
CmdExit=Ctrl+Q
CmdImportImage=
CmdImportImageSequence=
CmdImportSound=
CmdExportXsheet=Ctrl+Alt+X
CmdExportImageSequence=Ctrl+R
CmdExportImage=Ctrl+Shift+R
CmdExportMovie=
CmdExportPalette=
CmdExportSvgImage=
CmdExportSound=Ctrl+I
CmdUndo=Ctrl+Z
CmdRedo=Ctrl+Shift+Z
CmdCut=Ctrl+X
CmdCopy=Ctrl+C
CmdPaste=Ctrl+V
CmdSelectAll=Ctrl+A
CmdDeselectAll=Ctrl+D
CmdClearFrame=
CmdPreferences=
CmdResetWindows=
CmdZoomIn=Ctrl+Up
CmdZoomOut=Ctrl+Down
CmdRotateClockwise=R
CmdRotateAntiClosewise=Z
CmdResetZoomRotate=Ctrl+H
CmdFlipHorizontal=Shift+H
CmdFlipVertical=Shift+V
CmdPreview=Alt+P
CmdGrid=G
CmdOnionSkinPrevious=O
CmdOnionSkinNext=Alt+O
CmdPlay=Return
CmdLoop=Ctrl+L
CmdGotoNextFrame=
CmdGotoPreviousFrame=
CmdGotoNextKeyFrame=.
CmdGotoPreviousKeyFrame=","
CmdMoveFrameForward=Ctrl+.
CmdMoveFrameBackward=ctrl+","
CmdAddFrame=F7
CmdDuplicateFrame=F6
CmdRemoveFrame=Shift+F5
CmdToolMove=Q
CmdToolSelect=V
CmdToolBrush=B
CmdToolPolyline=Y
CmdToolSmudge=A
CmdToolPen=P
CmdToolHand=H
CmdToolPencil=N
CmdToolBucket=K
CmdToolEyedropper=I
CmdToolEraser=E
CmdNewBitmapLayer=Ctrl+Alt+B
CmdNewVectorLayer=Ctrl+Alt+V
CmdNewSoundLayer=Ctrl+Alt+W
CmdNewCameraLayer=Ctrl+Alt+C
CmdDeleteCurrentLayer=
CmdTogglePalette=C
CmdToggleToolBox=Ctrl+1
CmdToggleToolOptions=Ctrl+2
CmdToggleColorWheel=Ctrl+3
CmdToggleColorLibrary=Ctrl+4
CmdToggleDisplayOptions=Ctrl+5
CmdToggleTimeline=Ctrl+6

I guess  kb.ini file is some type of initialization file.
Anyway it’s referenced here:
kb_ini

 

 

So the magic search term at the moment is

// shortcuts command code
#define CMD_NEW_FILE  "CmdNewFile"
#define CMD_OPEN_FILE "CmdOpenFile"
#define CMD_SAVE_FILE "CmdSaveFile"
#define CMD_SAVE_AS "CmdSaveAs"
#define CMD_PRINT "CmdPrint"
#define CMD_EXIT "CmdExit"
#define CMD_IMPORT_IMAGE "CmdImportImage"
#define CMD_IMPORT_IMAGE_SEQ "CmdImportImageSequence"
#define CMD_IMPORT_MOVIE "CmdImportMovie"
#define CMD_IMPORT_PALETTE "CmdImportPalette"
#define CMD_IMPORT_SOUND "CmdImportSound"
#define CMD_EXPORT_XSHEET "CmdExportXsheet"
#define CMD_EXPORT_IMAGE_SEQ "CmdExportImageSequence"
#define CMD_EXPORT_IMAGE "CmdExportImage"
#define CMD_EXPORT_MOVIE "CmdExportMovie"
#define CMD_EXPORT_PALETTE "CmdExportPalette"
#define CMD_EXPORT_SVG "CmdExportSvgImage"
#define CMD_EXPORT_SOUND "CmdExportSound"
#define CMD_UNDO "CmdUndo"
#define CMD_REDO "CmdRedo"
#define CMD_CUT "CmdCut"
#define CMD_COPY "CmdCopy"
#define CMD_PASTE "CmdPaste"
#define CMD_SELECT_ALL "CmdSelectAll"
#define CMD_DESELECT_ALL "CmdDeselectAll"
#define CMD_CLEAR_FRAME "CmdClearFrame"
#define CMD_PREFERENCE "CmdPreferences"
#define CMD_RESET_WINDOWS "CmdResetWindows"
#define CMD_ZOOM_IN "CmdZoomIn"
#define CMD_ZOOM_OUT "CmdZoomOut"
#define CMD_ROTATE_CLOCK "CmdRotateClockwise"
#define CMD_ROTATE_ANTI_CLOCK "CmdRotateAntiClosewise"
#define CMD_RESET_ZOOM_ROTATE "CmdResetZoomRotate"
#define CMD_FLIP_HORIZONTAL "CmdFlipHorizontal"
#define CMD_FLIP_VERTICAL "CmdFlipVertical"
#define CMD_PREVIEW "CmdPreview"
#define CMD_GRID "CmdGrid"
#define CMD_ONIONSKIN_PREV "CmdOnionSkinPrevious"
#define CMD_ONIONSKIN_NEXT "CmdOnionSkinNext"
#define CMD_PLAY "CmdPlay"
#define CMD_LOOP "CmdLoop"
#define CMD_GOTO_NEXT_FRAME "CmdGotoNextFrame"
#define CMD_GOTO_PREV_FRAME "CmdGotoPreviousFrame"
#define CMD_GOTO_NEXT_KEY_FRAME "CmdGotoNextKeyFrame"
#define CMD_GOTO_PREV_KEY_FRAME "CmdGotoPreviousKeyFrame"
#define CMD_ADD_FRAME "CmdAddFrame"
#define CMD_DUPLICATE_FRAME "CmdDuplicateFrame"
#define CMD_REMOVE_FRAME "CmdRemoveFrame"
#define CMD_MOVE_FRAME_BACKWARD "CmdMoveFrameBackward"
#define CMD_MOVE_FRAME_FORWARD "CmdMoveFrameForward"
#define CMD_TOOL_MOVE "CmdToolMove"
#define CMD_TOOL_SELECT "CmdToolSelect"
#define CMD_TOOL_BRUSH "CmdToolBrush"
#define CMD_TOOL_POLYLINE "CmdToolPolyline"
#define CMD_TOOL_SMUDGE "CmdToolSmudge"
#define CMD_TOOL_PEN "CmdToolPen"
#define CMD_TOOL_HAND "CmdToolHand"
#define CMD_TOOL_PENCIL "CmdToolPencil"
#define CMD_TOOL_BUCKET "CmdToolBucket"
#define CMD_TOOL_EYEDROPPER "CmdToolEyedropper"
#define CMD_TOOL_ERASER "CmdToolEraser"
#define CMD_TOGGLE_PALETTE "CmdTogglePalette"
#define CMD_NEW_BITMAP_LAYER "CmdNewBitmapLayer"
#define CMD_NEW_VECTOR_LAYER "CmdNewVectorLayer"
#define CMD_NEW_SOUND_LAYER "CmdNewSoundLayer"
#define CMD_NEW_CAMERA_LAYER "CmdNewCameraLayer"
#define CMD_DELETE_CUR_LAYER "CmdDeleteCurrentLayer"
#define CMD_HELP "CmdHelp"
#define CMD_TOGGLE_TOOLBOX "CmdToggleToolBox"
#define CMD_TOGGLE_TOOL_OPTIONS "CmdToggleToolOptions"
#define CMD_TOGGLE_COLOR_WHEEL "CmdToggleColorWheel"
#define CMD_TOGGLE_COLOR_LIBRARY "CmdToggleColorLibrary"
#define CMD_TOGGLE_DISPLAY_OPTIONS "CmdToggleDisplayOptions"
#define CMD_TOGGLE_TIMELINE "CmdToggleTimeline"
#define CMD_INCREASE_SIZE "CmdIncreaseSize"
#define CMD_DECREASE_SIZE "CmdDecreaseSize"

// Save / Export
#define LAST_FILE_PATH          "LastFilePath"

// Settings Group/Key Name
#define PENCIL2D "Pencil"
#define SHORTCUTS_GROUP             "Shortcuts"
#define SETTING_AUTO_SAVE           "AutoSave"
#define SETTING_AUTO_SAVE_NUMBER    "AutosaveNumber"
#define SETTING_TOOL_CURSOR         "ToolCursors"
#define SETTING_HIGH_RESOLUTION     "HighResPosition"
#define SETTING_BACKGROUND_STYLE    "Background"
#define SETTING_WINDOW_OPACITY      "WindowOpacity"
#define SETTING_WINDOW_GEOMETRY     "WindowGeometry"
#define SETTING_WINDOW_STATE        "WindowState"
#define SETTING_CURVE_SMOOTHING     "CurveSmoothing"
#define SETTING_DISPLAY_EFFECT      "RenderEffect"
#define SETTING_SHORT_SCRUB         "ShortScrub"
#define SETTING_FRAME_SIZE          "FrameSize"
#define SETTING_TIMELINE_SIZE       "TimelineSize"
#define SETTING_LABEL_FONT_SIZE     "LabelFontSize"
#define SETTING_DRAW_LABEL          "DrawLabel"

#define SETTING_ANTIALIAS       "Antialiasing"
#define SETTING_SHOW_GRID       "ShowGrid"
#define SETTING_COUNT           "Count"
#define SETTING_SHADOW          "Shadow"
#define SETTING_PREV_ONION      "PrevOnion"
#define SETTING_NEXT_ONION      "NextOnion"
#define SETTING_AXIS            "Axis"
#define SETTING_CAMERABORDER    "CameraBorder"
#define SETTING_INVISIBLE_LINES "InvisibleLines"
#define SETTING_OUTLINES        "Outlines"
#define SETTING_ONION_BLUE      "OnionBlue"
#define SETTING_ONION_RED       "OnionRed"
#define SETTING_MIRROR_H        "MirrorH"
#define SETTING_MIRROR_V        "MirrorV"

#define SETTING_ONION_MAX_OPACITY       "OnionMaxOpacity"
#define SETTING_ONION_MIN_OPACITY       "OnionMinOpacity"
#define SETTING_ONION_PREV_FRAMES_NUM   "OnionPrevFramesNum"
#define SETTING_ONION_NEXT_FRAMES_NUM   "OnionNextFramesNum"
#define SETTING_ONION_TYPE              "OnionType"

#endif // PENCILDEF_H

I think we’re getting close

void MainWindow2::setupKeyboardShortcuts()
{
    checkExistingShortcuts();

    auto cmdKeySeq = []( QString strCommandName ) -> QKeySequence
    {
        strCommandName = QString( "shortcuts/" ) + strCommandName;
        QKeySequence keySequence( pencilSettings()->value( strCommandName ).toString() );
        return keySequence;
    };

    ui->actionNew->setShortcut( cmdKeySeq( CMD_NEW_FILE ) );
    ui->actionOpen->setShortcut( cmdKeySeq( CMD_OPEN_FILE ) );
    ui->actionSave->setShortcut( cmdKeySeq( CMD_SAVE_FILE ) );
    ui->actionSave_as->setShortcut( cmdKeySeq( CMD_SAVE_AS ) );
    ui->actionPrint->setShortcut( cmdKeySeq( CMD_PRINT ) );

    ui->actionImport_Image->setShortcut( cmdKeySeq( CMD_IMPORT_IMAGE ) );
    ui->actionImport_Image_Sequence->setShortcut( cmdKeySeq( CMD_IMPORT_IMAGE_SEQ ) );
    ui->actionImport_Movie->setShortcut( cmdKeySeq( CMD_IMPORT_MOVIE ) );
    ui->actionImport_Palette->setShortcut( cmdKeySeq( CMD_IMPORT_PALETTE ) );
    ui->actionImport_Sound->setShortcut( cmdKeySeq( CMD_IMPORT_SOUND ) );

    ui->actionExport_Image->setShortcut( cmdKeySeq( CMD_EXPORT_IMAGE ) );
    ui->actionExport_Image_Sequence->setShortcut( cmdKeySeq( CMD_EXPORT_IMAGE_SEQ ) );
    ui->actionExport_Movie->setShortcut( cmdKeySeq( CMD_EXPORT_MOVIE ) );
    ui->actionExport_Palette->setShortcut( cmdKeySeq( CMD_EXPORT_PALETTE ) );
    ui->actionExport_Svg_Image->setShortcut( cmdKeySeq( CMD_EXPORT_SVG ) );
    ui->actionExport_X_sheet->setShortcut( cmdKeySeq( CMD_EXPORT_XSHEET ) );

    // edit menu
    ui->actionUndo->setShortcut( cmdKeySeq( CMD_UNDO ) );
    ui->actionRedo->setShortcut( cmdKeySeq( CMD_REDO ) );
    ui->actionCut->setShortcut( cmdKeySeq( CMD_CUT ) );
    ui->actionCopy->setShortcut( cmdKeySeq( CMD_COPY ) );
    ui->actionPaste->setShortcut( cmdKeySeq( CMD_PASTE ) );
    ui->actionClearFrame->setShortcut( cmdKeySeq( CMD_CLEAR_FRAME ) );
    ui->actionSelect_All->setShortcut( cmdKeySeq( CMD_SELECT_ALL ) );
    ui->actionDeselect_All->setShortcut( cmdKeySeq( CMD_DESELECT_ALL ) );
    ui->actionPreference->setShortcut( cmdKeySeq( CMD_PREFERENCE ) );

    ui->actionReset_Windows->setShortcut( cmdKeySeq( CMD_RESET_WINDOWS ) );
    ui->actionReset_View->setShortcut( cmdKeySeq( CMD_RESET_ZOOM_ROTATE ) );
    ui->actionZoom_In->setShortcut( cmdKeySeq( CMD_ZOOM_IN ) );
    ui->actionZoom_Out->setShortcut( cmdKeySeq( CMD_ZOOM_OUT ) );
    ui->actionRotate_Clockwise->setShortcut( cmdKeySeq( CMD_ROTATE_CLOCK ) );
    ui->actionRotate_Anticlosewise->setShortcut( cmdKeySeq( CMD_ROTATE_ANTI_CLOCK ) );
    ui->actionHorizontal_Flip->setShortcut( cmdKeySeq( CMD_FLIP_HORIZONTAL ) );
    ui->actionVertical_Flip->setShortcut( cmdKeySeq( CMD_FLIP_VERTICAL ) );
    ui->actionPreview->setShortcut( cmdKeySeq( CMD_PREVIEW ) );
    ui->actionGrid->setShortcut( cmdKeySeq( CMD_GRID ) );
    ui->actionOnionPrevious->setShortcut( cmdKeySeq( CMD_ONIONSKIN_PREV ) );
    ui->actionOnionNext->setShortcut( cmdKeySeq( CMD_ONIONSKIN_NEXT ) );

    ui->actionPlay->setShortcut( cmdKeySeq( CMD_PLAY ) );
    ui->actionLoop->setShortcut( cmdKeySeq( CMD_LOOP ) );
    ui->actionPrevious_Frame->setShortcut( cmdKeySeq( CMD_GOTO_PREV_FRAME ) );
    ui->actionNext_Frame->setShortcut( cmdKeySeq( CMD_GOTO_NEXT_FRAME ) );
    ui->actionPrev_KeyFrame->setShortcut( cmdKeySeq( CMD_GOTO_PREV_KEY_FRAME ) );
    ui->actionNext_KeyFrame->setShortcut( cmdKeySeq( CMD_GOTO_NEXT_KEY_FRAME ) );
    ui->actionAdd_Frame->setShortcut( cmdKeySeq( CMD_ADD_FRAME ) );
    ui->actionDuplicate_Frame->setShortcut( cmdKeySeq( CMD_DUPLICATE_FRAME ) );
    ui->actionRemove_Frame->setShortcut( cmdKeySeq( CMD_REMOVE_FRAME ) );
    ui->actionMove_Frame_Backward->setShortcut( cmdKeySeq( CMD_MOVE_FRAME_BACKWARD ) );
    ui->actionMove_Frame_Forward->setShortcut( cmdKeySeq( CMD_MOVE_FRAME_FORWARD ) );

    ui->actionMove->setShortcut( cmdKeySeq( CMD_TOOL_MOVE ) );
    ui->actionSelect->setShortcut( cmdKeySeq( CMD_TOOL_SELECT ) );
    ui->actionBrush->setShortcut( cmdKeySeq( CMD_TOOL_BRUSH ) );
    ui->actionPolyline->setShortcut( cmdKeySeq( CMD_TOOL_POLYLINE ) );
    ui->actionSmudge->setShortcut( cmdKeySeq( CMD_TOOL_SMUDGE ) );
    ui->actionPen->setShortcut( cmdKeySeq( CMD_TOOL_PEN ) );
    ui->actionHand->setShortcut( cmdKeySeq( CMD_TOOL_HAND ) );
    ui->actionPencil->setShortcut( cmdKeySeq( CMD_TOOL_PENCIL ) );
    ui->actionBucket->setShortcut( cmdKeySeq( CMD_TOOL_BUCKET ) );
    ui->actionEyedropper->setShortcut( cmdKeySeq( CMD_TOOL_EYEDROPPER ) );
    ui->actionEraser->setShortcut( cmdKeySeq( CMD_TOOL_ERASER ) );

    ui->actionTogglePalette->setShortcut( cmdKeySeq( CMD_TOGGLE_PALETTE ) );
    //mScribbleArea->getPopupPalette()->closeButton->setText( tr("close/toggle (") + pencilSettings()->value( QString( "shortcuts/" ) + CMD_TOGGLE_PALETTE ).toString() + ")" );
    //mScribbleArea->getPopupPalette()->closeButton->setShortcut( cmdKeySeq( CMD_TOGGLE_PALETTE ) );

    ui->actionNew_Bitmap_Layer->setShortcut( cmdKeySeq( CMD_NEW_BITMAP_LAYER ) );
    ui->actionNew_Vector_Layer->setShortcut( cmdKeySeq( CMD_NEW_VECTOR_LAYER ) );
    ui->actionNew_Camera_Layer->setShortcut( cmdKeySeq( CMD_NEW_CAMERA_LAYER ) );
    ui->actionNew_Sound_Layer->setShortcut( cmdKeySeq( CMD_NEW_SOUND_LAYER ) );

    mToolBox->toggleViewAction()->setShortcut( cmdKeySeq( CMD_TOGGLE_TOOLBOX ) );
    mToolOptions->toggleViewAction()->setShortcut( cmdKeySeq( CMD_TOGGLE_TOOL_OPTIONS ) );
    mColorWheel->toggleViewAction()->setShortcut( cmdKeySeq( CMD_TOGGLE_COLOR_WHEEL ) );
    mColorPalette->toggleViewAction()->setShortcut( cmdKeySeq( CMD_TOGGLE_COLOR_LIBRARY ) );
    mTimeLine->toggleViewAction()->setShortcut( cmdKeySeq( CMD_TOGGLE_TIMELINE ) );
    mDisplayOptionWidget->toggleViewAction()->setShortcut( cmdKeySeq( CMD_TOGGLE_DISPLAY_OPTIONS ) );

    ui->actionHelp->setShortcut( cmdKeySeq( CMD_HELP ) );
}

Okkk… Now I think I found it.

void MainWindow2::createMenus()
{
    // ---------- File Menu -------------
    connect( ui->actionNew, &QAction::triggered, this, &MainWindow2::newDocument );
    connect( ui->actionOpen, &QAction::triggered, this, &MainWindow2::openDocument );
    connect( ui->actionSave_as, &QAction::triggered, this, &MainWindow2::saveAsNewDocument );
    connect( ui->actionSave, &QAction::triggered, this, &MainWindow2::saveDocument );
    connect( ui->actionExit, &QAction::triggered, this, &MainWindow2::close );

    /// --- Export Menu ---
    //connect( ui->actionExport_X_sheet, &QAction::triggered, mEditor, &Editor::exportX );
    connect( ui->actionExport_Image, &QAction::triggered, this, &MainWindow2::exportImage );
    connect( ui->actionExport_Image_Sequence, &QAction::triggered, this, &MainWindow2::exportImageSequence );
    connect( ui->actionExport_Movie, &QAction::triggered, this, &MainWindow2::exportMovie );

    connect( ui->actionExport_Palette, &QAction::triggered, this, &MainWindow2::exportPalette );

    /// --- Import Menu ---
    //connect( ui->actionExport_Svg_Image, &QAction::triggered, editor, &Editor::saveSvg );
    connect( ui->actionImport_Image, &QAction::triggered, this, &MainWindow2::importImage );
    connect( ui->actionImport_Image_Sequence, &QAction::triggered, this, &MainWindow2::importImageSequence );
    connect( ui->actionImport_Movie, &QAction::triggered, this, &MainWindow2::importMovie );

    connect( ui->actionImport_Sound, &QAction::triggered, mCommands, &CommandCenter::importSound );
    connect( ui->actionImport_Palette, &QAction::triggered, this, &MainWindow2::importPalette );

    /// --- Edit Menu ---
    ui->actionPreference->setMenuRole( QAction::PreferencesRole );

    connect( ui->actionUndo, &QAction::triggered, mEditor, &Editor::undo );
    connect( ui->actionRedo, &QAction::triggered, mEditor, &Editor::redo );
    connect( ui->actionCut, &QAction::triggered, mEditor, &Editor::cut );
    connect( ui->actionCopy, &QAction::triggered, mEditor, &Editor::copy );
    connect( ui->actionPaste, &QAction::triggered, mEditor, &Editor::paste );
    connect( ui->actionClearFrame, &QAction::triggered, mEditor, &Editor::clearCurrentFrame );
    connect( ui->actionFlip_X, &QAction::triggered, mCommands, &CommandCenter::flipX );
    connect( ui->actionFlip_Y, &QAction::triggered, mCommands, &CommandCenter::flipY );
    connect( ui->actionSelect_All, &QAction::triggered, mEditor, &Editor::selectAll );
    connect( ui->actionDeselect_All, &QAction::triggered, mEditor, &Editor::deselectAll );
    connect( ui->actionPreference, &QAction::triggered, [=] { preferences(); } );

    ui->actionRedo->setEnabled( false );

    /// --- Layer Menu ---
    connect( ui->actionNew_Bitmap_Layer, &QAction::triggered, mCommands, &CommandCenter::addNewBitmapLayer );
    connect( ui->actionNew_Vector_Layer, &QAction::triggered, mCommands, &CommandCenter::addNewVectorLayer );
    connect( ui->actionNew_Sound_Layer, &QAction::triggered, mCommands, &CommandCenter::addNewSoundLayer );
    connect( ui->actionNew_Camera_Layer, &QAction::triggered, mCommands, &CommandCenter::addNewCameraLayer );
    connect( ui->actionDelete_Current_Layer, &QAction::triggered, mEditor->layers(), &LayerManager::deleteCurrentLayer );

    /// --- View Menu ---
    connect( ui->actionZoom_In,  &QAction::triggered, mCommands, &CommandCenter::ZoomIn );
    connect( ui->actionZoom_Out, &QAction::triggered, mCommands, &CommandCenter::ZoomOut );
    connect( ui->actionRotate_Clockwise, &QAction::triggered, mCommands, &CommandCenter::rotateClockwise );
    connect( ui->actionRotate_Anticlosewise, &QAction::triggered, mCommands, &CommandCenter::rotateCounterClockwise );
    connect( ui->actionReset_Windows, &QAction::triggered, this, &MainWindow2::dockAllSubWidgets );
    connect( ui->actionReset_View, &QAction::triggered, mEditor->view(), &ViewManager::resetView );
    connect( ui->actionHorizontal_Flip, &QAction::triggered, mEditor, &Editor::toggleMirror );
    connect( ui->actionVertical_Flip, &QAction::triggered, mEditor, &Editor::toggleMirrorV );

    ui->actionPreview->setEnabled( false );
    //# connect(previewAct, SIGNAL(triggered()), editor, SLOT(getCameraLayer()));//TODO: Preview view

    setMenuActionChecked( ui->actionGrid, mEditor->preference()->isOn( SETTING::GRID ) );
    connect( ui->actionGrid, &QAction::triggered, mCommands, &CommandCenter::showGrid );

    connect( ui->actionOnionPrevious, &QAction::triggered, mEditor, &Editor::toggleOnionPrev );
    connect( ui->actionOnionNext, &QAction::triggered, mEditor, &Editor::toggleOnionNext );
    connect( ui->actionMultiLayerOnionSkin, &QAction::triggered, mEditor, &Editor::toggleMultiLayerOnionSkin );

    //connect( mEditor, &Editor::onionPrevChanged, ui->actionOnionPrevious, &QAction::setChecked );
    //connect( mEditor, &Editor::onionNextChanged, ui->actionOnionNext, &QAction::setChecked );
    
    connect( mEditor, SIGNAL(multiLayerOnionSkinChanged(bool)), ui->actionMultiLayerOnionSkin, SLOT(setChecked(bool)));

    /// --- Animation Menu ---
    PlaybackManager* pPlaybackManager = mEditor->playback();
    connect( ui->actionPlay, &QAction::triggered, mCommands, &CommandCenter::PlayStop );

    connect( ui->actionLoop, &QAction::triggered, pPlaybackManager, &PlaybackManager::setLooping );
    connect( ui->actionLoopControl, &QAction::triggered, pPlaybackManager, &PlaybackManager::enableRangedPlayback );
    connect( pPlaybackManager, &PlaybackManager::loopStateChanged, ui->actionLoop, &QAction::setChecked );
    connect( pPlaybackManager, &PlaybackManager::rangedPlaybackStateChanged, ui->actionLoopControl, &QAction::setChecked );

    connect(ui->actionAdd_Frame, &QAction::triggered, mEditor, &Editor::addNewKey );
    connect(ui->actionRemove_Frame, &QAction::triggered, mEditor, &Editor::removeKey );
    //connect(ui->actionNext_Frame, &QAction::triggered, m_pEditor, &Editor::playNextFrame );
    //connect(ui->actionPrevious_Frame, &QAction::triggered, m_pEditor, &Editor::playPrevFrame );
    connect(ui->actionNext_KeyFrame, &QAction::triggered, mEditor, &Editor::scrubNextKeyFrame );
    connect(ui->actionPrev_KeyFrame, &QAction::triggered, mEditor, &Editor::scrubPreviousKeyFrame );
    connect(ui->actionDuplicate_Frame, &QAction::triggered, mEditor, &Editor::duplicateKey );
    connect(ui->actionMove_Frame_Forward, &QAction::triggered, mEditor, &Editor::moveFrameForward ); //HERE
    connect(ui->actionMove_Frame_Backward, &QAction::triggered, mEditor, &Editor::moveFrameBackward );

    /// --- Tool Menu ---
    connect(ui->actionMove, &QAction::triggered, mToolBox, &ToolBoxWidget::moveOn );
    connect(ui->actionSelect, &QAction::triggered, mToolBox, &ToolBoxWidget::selectOn );
    connect(ui->actionBrush, &QAction::triggered, mToolBox, &ToolBoxWidget::brushOn );
    connect(ui->actionPolyline, &QAction::triggered, mToolBox, &ToolBoxWidget::polylineOn );
    connect(ui->actionSmudge, &QAction::triggered, mToolBox, &ToolBoxWidget::smudgeOn );
    connect(ui->actionPen, &QAction::triggered, mToolBox, &ToolBoxWidget::penOn );
    connect(ui->actionHand, &QAction::triggered, mToolBox, &ToolBoxWidget::handOn );
    connect(ui->actionPencil, &QAction::triggered, mToolBox, &ToolBoxWidget::pencilOn );
    connect(ui->actionBucket, &QAction::triggered, mToolBox, &ToolBoxWidget::bucketOn );
    connect(ui->actionEyedropper, &QAction::triggered, mToolBox, &ToolBoxWidget::eyedropperOn );
    connect(ui->actionEraser, &QAction::triggered, mToolBox, &ToolBoxWidget::eraserOn );
    connect(ui->actionTogglePalette, &QAction::triggered, mScribbleArea,&ScribbleArea::togglePopupPalette );
    connect(ui->actionResetToolsDefault, &QAction::triggered, mEditor->tools(), &ToolManager::resetAllTools );

    /// --- Window Menu ---
    QMenu* winMenu = ui->menuWindows;

    QAction* actions[] =
    {
        mToolBox->toggleViewAction(),
        mToolOptions->toggleViewAction(),
        mColorWheel->toggleViewAction(),
        mColorPalette->toggleViewAction(),
        mTimeLine->toggleViewAction(),
        mDisplayOptionWidget->toggleViewAction()
    };
    winMenu->clear();
    for ( QAction* action : actions )
    {
        action->setMenuRole( QAction::NoRole );
        winMenu->addAction( action );
    }

    /// --- Help Menu ---
    connect( ui->actionHelp, &QAction::triggered, this, &MainWindow2::helpBox);
    connect( ui->actionAbout, &QAction::triggered, this, &MainWindow2::aboutPencil );

    // --------------- Menus ------------------
    mRecentFileMenu = new RecentFileMenu( tr("Open Recent"), this );
    mRecentFileMenu->loadFromDisk();
    ui->menuFile->insertMenu( ui->actionSave, mRecentFileMenu );

    connect( mRecentFileMenu, &RecentFileMenu::loadRecentFile, this, &MainWindow2::openFile );

    //connect( ui->menuEdit, SIGNAL( aboutToShow() ), this, SLOT( undoActSetText() ) );
    //connect( ui->menuEdit, SIGNAL( aboutToHide() ), this, SLOT( undoActSetEnabled() ) );
}

 

void Editor::duplicateKey()
{
	Layer* layer = mObject->getLayer( layers()->currentLayerIndex() );
	if ( layer != NULL )
	{
        if ( layer->type() == Layer::VECTOR || layer->type() == Layer::BITMAP )
		{
            // Will copy the selection if any or the entire image if there is none
            //
            if(!mScribbleArea->somethingSelected) {
                mScribbleArea->selectAll();
            }

            copy();
            addNewKey();
            paste();

            mScribbleArea->setModified( layers()->currentLayerIndex(), currentFrame() );
            mScribbleArea->update();
		}
	}
}

I just did some testing and everything within this routine is functioning properly.

(This seemed like more work than it should have been)
My think the next step is to see where pasting is occuring.   The critters are laying in there some where.
(Well the morning is blown and nothing really fixed yet.)
I think my next steps are probably to isolate the wacko behavior and put a break point on paste() so I can see what’s going on.

 

Posted in Uncategorized | Tagged | Leave a comment

Messing around with Pencil2d. Exploring the selection tool.

Over the holidays, my daughter had expressed an interest in learning animation.  I did a little googling an I found a open source package called pencil2d.   She downloaded and tried using it and found it too buggy.  I uses C++/QT which is something I’m familiar with my dabbling with freecad so, I figured what the heck and I downloaded the source from git..
My understanding is that the original developer of pencil dropped the project and it basically forked. Pencil2d is a merge and enhancement effort.
I don’t have a lot of open bandwidth these days, but I’m hoping that contribute a bit to this project and get it to a point, where my daughter can make some use full animations.

From late December to today, it seems like things have improved quite a bit with pencil.   One very annoying thing at the moment is the selection tool.. It seems to be misbehaving…
https://github.com/pencil2d/pencil/issues/414

Basically when copy pasting with the selection tool very bizarre things occur.  It’s sort of hard to explain.  At this point I just want to see if can get a 30K foot understanding of how things are supposed to work.
So I think the problem has to do with thinks interacting  with the selection tool versus the selection tool itself. https://github.com/pencil2d/pencil/blob/master/core_lib/tool/selecttool.h git activity around selecttool.h has been quite.. (last change 25 days ago..) So, need to see how this object is used.
So.. Looking for reference to the SelectTool we get this:

usageOfSelectTool

So the selecttool seems to interact with the world with the toolmanager..

 

 

 

So.. It looks like SelectTool object is stored in mToolSetHash.

mToolSetHash

 

 

 

 

 

QHash looks alot to me like a Dictionary in C#  which contains a key and a value.
The key is an enumeration called ToolType and Value is a pointer to the BaseClass definition of the tools..
Ok.. So the enumeration ToolType is defined here:
https://github.com/pencil2d/pencil/blob/master/core_lib/util/pencildef.h

Ok.. This is very understandable to me… So.. to figure out where the selectTool object is interacting with the rest of pencil2d we need to search on the Enumeration SELECT  (good thing that we’re not doing SQL in this app)
We need to find the references to the enumeration SELECT
Select

 

 

 


————————————————————

void ScribbleArea::paintEvent( QPaintEvent* event )
{
    //qCDebug( mLog ) << "Paint event!" << QDateTime::currentDateTime() << event->rect();

    if ( !mMouseInUse || currentTool()->type() == MOVE || currentTool()->type() == HAND )
    {
        // --- we retrieve the canvas from the cache; we create it if it doesn't exist
        int curIndex = mEditor->currentFrame();
        int frameNumber = mEditor->layers()->LastFrameAtFrame( curIndex );

        QString strCachedFrameKey = "frame" + QString::number( frameNumber );
        if ( !QPixmapCache::find( strCachedFrameKey, mCanvas ) )
        {
            drawCanvas( mEditor->currentFrame(), event->rect() );
            QPixmapCache::insert( strCachedFrameKey, mCanvas );
            //qDebug() << "Repaint canvas!";
        }
    }

    if ( currentTool()->type() == MOVE )
    {
        Layer* layer = mEditor->layers()->currentLayer();
        Q_CHECK_PTR( layer );
        if ( layer->type() == Layer::VECTOR )
        {
            auto vecLayer = static_cast< LayerVector* >( layer );
            vecLayer->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 )->setModified( true );
        }
    }

    QPainter painter( this );

    // paints the canvas
    painter.setWorldMatrixEnabled( false );
    //painter.setTransform( transMatrix ); // FIXME: drag canvas by hand

    painter.drawPixmap( QPoint( 0, 0 ), mCanvas );


    Layer* layer = mEditor->layers()->currentLayer();

    if ( !editor()->playback()->isPlaying() )    // we don't need to display the following when the animation is playing
    {
        if ( layer->type() == Layer::VECTOR )
        {
            VectorImage *vectorImage = ( ( LayerVector * )layer )->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );

            switch ( currentTool()->type() )
            {
                case SMUDGE:
                case HAND:
                {
                    painter.save();
                    painter.setWorldMatrixEnabled( false );
                    painter.setRenderHint( QPainter::Antialiasing, false );
                    // ----- paints the edited elements
                    QPen pen2( Qt::black, 0.5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin );
                    painter.setPen( pen2 );
                    QColor colour;
                    // ------------ vertices of the edited curves
                    colour = QColor( 200, 200, 200 );
                    painter.setBrush( colour );
                    for ( int k = 0; k < vectorSelection.curve.size(); k++ )
                    {
                        int curveNumber = vectorSelection.curve.at( k );

                        for ( int vertexNumber = -1; vertexNumber < vectorImage->getCurveSize( curveNumber ); vertexNumber++ )
                        {
                            QPointF vertexPoint = vectorImage->getVertex( curveNumber, vertexNumber );
                            QRectF rectangle( mEditor->view()->mapCanvasToScreen( vertexPoint ) - QPointF( 3.0, 3.0 ), QSizeF( 7, 7 ) );
                            if ( rect().contains( mEditor->view()->mapCanvasToScreen( vertexPoint ).toPoint() ) )
                            {
                                painter.drawRect( rectangle );
                            }
                        }
                    }
                    // ------------ selected vertices of the edited curves
                    colour = QColor( 100, 100, 255 );
                    painter.setBrush( colour );
                    for ( int k = 0; k < vectorSelection.vertex.size(); k++ )
                    {
                        VertexRef vertexRef = vectorSelection.vertex.at( k );
                        QPointF vertexPoint = vectorImage->getVertex( vertexRef );
                        QRectF rectangle0 = QRectF( mEditor->view()->mapCanvasToScreen( vertexPoint ) - QPointF( 3.0, 3.0 ), QSizeF( 7, 7 ) );
                        painter.drawRect( rectangle0 );
                    }
                    // ----- paints the closest vertices
                    colour = QColor( 255, 0, 0 );
                    painter.setBrush( colour );
                    if ( vectorSelection.curve.size() > 0 )
                    {
                        for ( int k = 0; k < mClosestVertices.size(); k++ )
                        {
                            VertexRef vertexRef = mClosestVertices.at( k );
                            QPointF vertexPoint = vectorImage->getVertex( vertexRef );

                            QRectF rectangle = QRectF( mEditor->view()->mapCanvasToScreen( vertexPoint ) - QPointF( 3.0, 3.0 ), QSizeF( 7, 7 ) );
                            painter.drawRect( rectangle );
                        }
                    }
                    painter.restore();
                    break;
                }

                case MOVE:
                {
                    // ----- paints the closest curves
                    mBufferImg->clear();
                    QColor colour = QColor( 100, 100, 255, 50 );
                    QPen pen2( colour, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin );


                    for ( int k = 0; k < mClosestCurves.size(); k++ )
                    {
                        float scale = mEditor->view()->scaling(); // FIXME: check whether it's correct (det = area?)

                        int idx = mClosestCurves[ k ];
                        if ( vectorImage->m_curves.size() <= idx )
                        {
                            // safety check
                            continue;
                        }
                        BezierCurve myCurve = vectorImage->m_curves[ mClosestCurves[ k ] ];
                        if ( myCurve.isPartlySelected() )
                        {
                            myCurve.transform( selectionTransformation );
                        }
                        QPainterPath path = myCurve.getStrokedPath( 1.2 / scale, false );
                        mBufferImg->drawPath( mEditor->view()->mapCanvasToScreen( path ),
                                              pen2,
                                              colour,
                                              QPainter::CompositionMode_SourceOver,
                                              mPrefs->isOn( SETTING::ANTIALIAS ) );
                    }
                    break;
                }
            } // end siwtch
        }

        // paints the  buffer image
        if ( mEditor->layers()->currentLayer() != NULL )
        {
            painter.setOpacity( 1.0 );
            if ( mEditor->layers()->currentLayer()->type() == Layer::BITMAP )
            {
                painter.setWorldMatrixEnabled( true );
                painter.setTransform( mEditor->view()->getView() );
            }
            else if ( mEditor->layers()->currentLayer()->type() == Layer::VECTOR )
            {
                painter.setWorldMatrixEnabled( false );
            }

            //qCDebug( mLog ) << "BufferRect" << mBufferImg->bounds();

            mBufferImg->paintImage( painter );
        }

        // paints the selection outline
        if ( somethingSelected && ( myTempTransformedSelection.isValid() || mMoveMode == ROTATION ) ) // @revise
        {
            // outline of the transformed selection
            painter.setWorldMatrixEnabled( false );
            painter.setOpacity( 1.0 );
            QPolygon tempRect = mEditor->view()->getView().mapToPolygon( myTempTransformedSelection.normalized().toRect() );

            Layer* layer = mEditor->layers()->currentLayer();
            if ( layer != NULL )
            {
                if ( layer->type() == Layer::BITMAP )
                {
                    painter.setBrush( Qt::NoBrush );
                    painter.setPen( Qt::DashLine );
                }
                if ( layer->type() == Layer::VECTOR )
                {
                    painter.setBrush( QColor( 0, 0, 0, 20 ) );
                    painter.setPen( Qt::gray );
                }
                painter.drawPolygon( tempRect );

                if ( layer->type() != Layer::VECTOR || currentTool()->type() != SELECT )
                {
                    painter.setPen( Qt::SolidLine );
                    painter.setBrush( QBrush( Qt::gray ) );
                    painter.drawRect( tempRect.point( 0 ).x() - 3, tempRect.point( 0 ).y() - 3, 6, 6 );
                    painter.drawRect( tempRect.point( 1 ).x() - 3, tempRect.point( 1 ).y() - 3, 6, 6 );
                    painter.drawRect( tempRect.point( 2 ).x() - 3, tempRect.point( 2 ).y() - 3, 6, 6 );
                    painter.drawRect( tempRect.point( 3 ).x() - 3, tempRect.point( 3 ).y() - 3, 6, 6 );
                }
            }
        }
    }

    // clips to the frame of the camera
    if ( mPrefs->isOn( SETTING::CAMERABORDER ) )
    {
        QRect rect = ( ( LayerCamera * )mEditor->object()->getLayer(mEditor->layers()->getLastCameraLayer()) )->getViewRect();
        rect.translate( width() / 2, height() / 2 );
        painter.setWorldMatrixEnabled( false );
        painter.setPen( Qt::NoPen );
        painter.setBrush( QColor( 0, 0, 0, 160 ) );
        painter.drawRect( QRect( 0, 0, width(), ( height() - rect.height() ) / 2 ) );
        painter.drawRect( QRect( 0, rect.bottom(), width(), ( height() - rect.height() ) / 2 ) );
        painter.drawRect( QRect( 0, rect.top(), ( width() - rect.width() ) / 2, rect.height() - 1 ) );
        painter.drawRect( QRect( ( width() + rect.width() ) / 2, rect.top(), (( width() - rect.width() ) / 2) + 1, rect.height() - 1 ) );
        painter.setPen( Qt::black );
        painter.setBrush( Qt::NoBrush );
        painter.drawRect( QRect(rect.x(), rect.y(), rect.width() - 1, rect.height() - 1) );
    }

    // outlines the frame of the viewport
#ifdef _DEBUG
    painter.setWorldMatrixEnabled( false );
    painter.setPen( QPen( Qt::gray, 2 ) );
    painter.setBrush( Qt::NoBrush );
    painter.drawRect( QRect( 0, 0, width(), height() ) );
#endif

    event->accept();
}

 

This seems like its doing something with rotation? (I’ve heard this mentioned)

————————————————————

Void ToolBoxWidget::selectOn()
{
    editor()->tools()->setCurrentTool( SELECT );

    deselectAllTools();
    selectButton->setChecked(true);
}

Ok.. I think this is fired to select the gui tool (this might be worth looking at later

void ToolBoxWidget::setCurrentTool( ToolType toolType )
{
    switch(toolType)
    {
    case PENCIL:
        emit pencilOn();
        break;
    case ERASER:
        emit eraserOn();
        break;
    case SELECT:
        emit selectOn();
        break;
    case MOVE:
        emit moveOn();
        break;
    case HAND:
        emit handOn();
        break;
    case SMUDGE:
        emit smudgeOn();
        break;
    case PEN:
        emit penOn();
        break;
    case POLYLINE:
        emit polylineOn();
        break;
    case BUCKET:
        emit bucketOn();
        break;
    case EYEDROPPER:
        emit eyedropperOn();
        break;
    case BRUSH:
        emit brushOn();
        break;
    default:
        break;
    }
}

Ok.. it looks like we’re just selecting the  Selection tool to be the default object here.

———————————————————–

bool ToolManager::init()
{
    mIsSwitchedToEraser = false;

    mToolSetHash.insert( PEN, new PenTool );
    mToolSetHash.insert( PENCIL, new PencilTool );
    mToolSetHash.insert( BRUSH, new BrushTool );
    mToolSetHash.insert( ERASER, new EraserTool );
    mToolSetHash.insert( BUCKET, new BucketTool );
    mToolSetHash.insert( EYEDROPPER, new EyedropperTool );
    mToolSetHash.insert( HAND, new HandTool );
    mToolSetHash.insert( MOVE, new MoveTool );
    mToolSetHash.insert( POLYLINE, new PolylineTool );
    mToolSetHash.insert( SELECT, new SelectTool );
    mToolSetHash.insert( SMUDGE, new SmudgeTool );

    foreach( BaseTool* pTool, mToolSetHash.values() )
    {
        pTool->initialize( editor() );
    }

    setDefaultTool();

    return true;
}

I can see putting some break points to get a look at the call stack at a few stops at some point

—————————————————————————–

QString BaseTool::TypeName( ToolType type )
{
    static QMap<ToolType, QString>* map = NULL;

    if ( map == NULL )
    {
        map = new QMap<ToolType, QString>();
        map->insert( PENCIL, "Pencil" );
        map->insert( ERASER, "Eraser" );
        map->insert( SELECT, "Select" );
        map->insert( MOVE, "Move" );
        map->insert( HAND, "Hand" );
        map->insert( SMUDGE, "Smudge" );
        map->insert( PEN, "Pen" );
        map->insert( POLYLINE, "Polyline" );
        map->insert( BUCKET, "Bucket" );
        map->insert( EYEDROPPER, "Eyedropper" );
        map->insert( BRUSH, "Brush" );
    }

    return map->value( type );
}

Can’t see anything be an issue here with what I’m working on.

————————————————

void MoveTool::mouseMoveEvent( QMouseEvent *event )
{
    Layer* layer = mEditor->layers()->currentLayer();
    if ( layer == NULL ) {
        return;
    }

    if ( layer->type() != Layer::BITMAP && layer->type() != Layer::VECTOR )
    {
        return;
    }

    if ( event->buttons() & Qt::LeftButton )   // the user is also pressing the mouse (dragging)
    {
        if ( mScribbleArea->somethingSelected )     // there is something selected
        {
            qreal xOffset = mScribbleArea->mOffset.x();
            qreal yOffset = mScribbleArea->mOffset.y();

            if ( event->modifiers() == Qt::ShiftModifier )    // (makes resize proportional, move linear)
            {
                qreal factor = mScribbleArea->mySelection.width() / mScribbleArea->mySelection.height();

                if (mScribbleArea->mMoveMode == ScribbleArea::TOPLEFT || mScribbleArea->mMoveMode == ScribbleArea::BOTTOMRIGHT) {
                    yOffset = xOffset / factor;
                }
                else if (mScribbleArea->mMoveMode == ScribbleArea::TOPRIGHT || mScribbleArea->mMoveMode == ScribbleArea::BOTTOMLEFT) {
                    yOffset = -(xOffset / factor);
                }
                else if (mScribbleArea->mMoveMode == ScribbleArea::MIDDLE) {

                    qreal absX = xOffset;
                    if (absX < 0) {absX = -absX;}

                    qreal absY = yOffset;
                    if (absY < 0) {absY = -absY;}

                    if (absX > absY) {
                        yOffset = 0;
                    }
                    if (absY > absX) {
                        xOffset = 0;
                    }
                }
            }

            switch ( mScribbleArea->mMoveMode )
            {
            case ScribbleArea::MIDDLE:
                if ( QLineF( getLastPressPixel(), getCurrentPixel() ).length() > 4 )
                {
                    mScribbleArea->myTempTransformedSelection = mScribbleArea->myTransformedSelection.translated( QPointF(xOffset, yOffset) );
                }
                break;

            case ScribbleArea::TOPRIGHT:
                mScribbleArea->myTempTransformedSelection =
                        mScribbleArea->myTransformedSelection.adjusted( 0, yOffset, xOffset, 0 );
                break;


            case ScribbleArea::TOPLEFT:
                mScribbleArea->myTempTransformedSelection =
                        mScribbleArea->myTransformedSelection.adjusted( xOffset, yOffset, 0, 0 );
                break;

                // TOPRIGHT XXX

            case ScribbleArea::BOTTOMLEFT:
                mScribbleArea->myTempTransformedSelection =
                        mScribbleArea->myTransformedSelection.adjusted( xOffset, 0, 0, yOffset );
                break;

            case ScribbleArea::BOTTOMRIGHT:
                mScribbleArea->myTempTransformedSelection =
                        mScribbleArea->myTransformedSelection.adjusted( 0, 0, xOffset, yOffset );
                break;
            case ScribbleArea::ROTATION:
                mScribbleArea->myTempTransformedSelection =
                        mScribbleArea->myTransformedSelection; // @ necessary?
                mScribbleArea->myRotatedAngle = getCurrentPixel().x() - getLastPressPixel().x();
                //qDebug() << "rotation" << m_pScribbleArea->myRotatedAngle;
                break;
            }

            mScribbleArea->calculateSelectionTransformation();

            mScribbleArea->paintTransformedSelection();
        }
        else     // there is nothing selected
        {
            // we switch to the select tool
            mEditor->tools()->setCurrentTool( SELECT );
            mScribbleArea->mMoveMode = ScribbleArea::MIDDLE;
            mScribbleArea->mySelection.setTopLeft( getLastPoint() );
            mScribbleArea->mySelection.setBottomRight( getLastPoint() );
            mScribbleArea->setSelection( mScribbleArea->mySelection, true );
        }
    }
    else     // the user is moving the mouse without pressing it
    {
        if ( layer->type() == Layer::VECTOR )
        {
            auto layerVector = static_cast< LayerVector* >( layer );
            VectorImage* pVecImg = layerVector->getLastVectorImageAtFrame( mEditor->currentFrame(), 0 );
            mScribbleArea->mClosestCurves = pVecImg->getCurvesCloseTo( getCurrentPoint(), mScribbleArea->tol / mEditor->view()->scaling() );
        }
        mScribbleArea->update();
    }
}

bool MoveTool::keyPressEvent(QKeyEvent *event)
{
    switch ( event->key() ) {
    case Qt::Key_Escape:
        cancelChanges();
        break;
    default:
        break;
    }

    // Follow the generic behaviour anyway
    //
    return false;
}

This is interesting and I think will need disection at some point.

—————————————————————

ToolType SelectTool::type()
{
    return SELECT;
}

This is a virtual functions defined in the baseclasstool and overwritten in

—————————————————-

enum ToolType
{
    INVALID_TOOL = -1,
    PENCIL = 0,
    ERASER,
    SELECT,
    MOVE,
    HAND,
    SMUDGE,
    PEN,
    POLYLINE,
    BUCKET,
    EYEDROPPER,
    BRUSH
};

The enumeration which defines SELECT.

Basically.. I just want to how the select object is use… I’m not trying to fix anything yet.

There’s a lot going on here and hopefully this will save me a little time later on.
One thing that I need to study is the copy paste mechanism…
(One thing that I notice is the duplicate frame F6 has a behavior that is weird at first but essenetially correct when you think about it.(Well maybe) When you hit f6 you get the image put into the new frame with the selection box around it.   If you draw on the frame and hit F6 the new stuff you just drew doesn’t copy over…  you need to hit escape and then F6… (I’m thinking this will come in handy at some point)

 

Posted in Uncategorized | Tagged | Leave a comment

Nice little git reference.

I seem to gotten back on the git thing..  Anyway, I found this nice concise guide on forking and pushing changes…   Kind of nice so I wanting to leave myself a note….

http://kbroman.org/github_tutorial/pages/fork.html

Posted in Uncategorized | Tagged | Leave a comment

First dabbles with Android-Studio

A while back I was dabbling with App-Inventor to get my hexy the hexapod working on my Nexus tablet.  It was a fun project.   Poor hexy is in pieces now, stripped for her servo’s.    So.. for a variety of reasons, I sort of have an itch for looking at android programming again.

Doing a fair amount of googling, It looks like the thing to try is Android- Studio…  Ok.
So I ran across setting up a android-studio on ubuntu via a PPA here:
http://www.webupd8.org/2014/05/install-android-studio-in-ubuntu-via-ppa.html?m=1

It took me a few minutes to figure out how to fire the darn thing up.

jonasthomas@JTLapTop:~$ /opt/android-studio/bin/studio.sh

So you think I be ready to fly….. nope.

JDK Required: ‘tools.jar’ seems to be not in Studio classpath.
Please ensure JAVA_HOME points to JDK rather than JRE.

Ok…  So an interesting post on here
JDK = JRE + Development tools.

JRE = JVM + Libraries

JDK = {JVM + Libraries} + Development tools.

On the Tools.Jar I found a nice post here: http://stackoverflow.com/questions/17474963/android-studio-tools-jar-file-is-not-present-in-classpath

Hmm
jonasthomas@JTLapTop:~$ java -version
java version “1.7.0_79”
OpenJDK Runtime Environment (IcedTea 2.5.6) (7u79-2.5.6-0ubuntu1.14.04.1)
OpenJDK 64-Bit Server VM (build 24.79-b02, mixed mode)
jonasthomas@JTLapTop:~$ java -version
java version “1.7.0_79”
OpenJDK Runtime Environment (IcedTea 2.5.6) (7u79-2.5.6-0ubuntu1.14.04.1)
OpenJDK 64-Bit Server VM (build 24.79-b02, mixed mode)
jonasthomas@JTLapTop:~$

Ok.. I have OpenJDK installed (Possibly when I installed Eclipse-Arduino)  This seems to be working ok.. So I don’t want to hose that up.
Soo.. The previous post pointed me to here:http://arwankhoiruddin.blogspot.co.il/2014/01/android-studio-in-ubuntu-problem.html

jonasthomas@JTLapTop:~$ update-alternatives –config java
There are 3 choices for the alternative java (providing /usr/bin/java).

Selection    Path                                            Priority   Status
————————————————————
* 0            /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java   1071      auto mode
1            /usr/bin/gij-4.8                                 1048      manual mode
2            /usr/lib/jvm/java-6-openjdk-amd64/jre/bin/java   1061      manual mode
3            /usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java   1071      manual mode

Press enter to keep the current choice[*], or type selection number:

Ok.. I think all I need to do is to a Path to studio.sh

Soo.. I added this to the top:
export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64/jre/bin/java

Well that didn’t work.
Some talked about just doing this.
http://stackoverflow.com/questions/17033726/android-studio-error-after-studio-sh

sudo apt-get install openjdk-7-jdk

Ok… So that did it…
Its a bit strange that seems to have worked.  I thought that I had the jdk already installed, but when I did the installed it didn’t give me a message back that it was installed already and libraries started loading up.. Go figure.

Posted in Uncategorized | Leave a comment

More Fooling speed encoder with my little robot.

The little robot I picked up a Musecon came supplied with an optical encoder disks but lacked the transmissive optical sensor with phototransistor output, photo sensor, or whatever you call those things.  Dwayne had a sensor for paired robot wheels that I got from him, but they were not wide enough to fit the laser cut plastic of the kit.   I had a heck of time finding the magic search terms for Ali-Express to potentially find an exact match to the kit.  I did manage to find the magic incantation eventually, http://www.aliexpress.com/item/Velocimetry-sensors-sensor-module-encoder-barrowload-speed-robot/1266192518.html

For now, I think I’m going to just doing to 3d print a new platform to match the photosensor sensor span I have  and well as optimized it to  the mission of my bot. Its mission basically to swiffer my wood floors and drag the stuff to one end of the floor to be picked up by something else.  At this point, I think I want my bot to drag a swiffer pad behind it and have a ping sensor in front of it connected to a hobby servo to perform a sweeping scan.

I think I need the speed sensors to help keep the bot moving on a straight line. When it reaches one end of the floor, I want to lift the swiffer off the floor so the dust stays at one end.. (At this point I’ll either just pick it up with a dust pan, or have a Rhomba just focus on that).  One thing, I haven’t worked out is how to map objects in the room, and to map a path.. (The ping I think can avoid crashes, but I don’t think it’s enough)

Anyway this is the velocimetry I got to play with at the moment.  It was labeled as KeyesEye.  I did a fair amount of googling to find a spec sheet but came up dry.

photosensorboardA

 

 

 

The photosensor is labeled as MOC70T3..  I couldn’t get a match on the T3 but I think this is close enough.  http://www.alldatasheet.com/datasheet-pdf/pdf/53870/FAIRCHILD/MOC70P3.html so… it guess the correct term is to describe the sensors is PHOTOTRANSISTOR OPTICAL INTERRUPTER SWITCH.
The main chip on the board is labeled:
74HC14D
L3D8T208
Un01345F
I got a hit on the first number http://www.nxp.com/documents/data_sheet/74HC_HCT14.pdf Hex inverting Schmitt trigger.  So basically at then end of all this, I should basically just be able to plug this into an arduino and it should just work.

So I’m interested in figuring out how to setup some type of PID for controlling two DC motors using my arduino,  googling I came up with a really cool link for a diy baby segway (which is way more complicated that what I’m interested in doing (at this point anyway) http://forum.arduino.cc/index.php?topic=8652.0
So.. I think for my little project, I’m going to be generating some interupts from the Optical interrupters to keep this puppy driving in a straight line.
So the project at this point, is going to be using a bluetooth module, 2 dcmotors, two servos, one ping sensor, and photo sensors.
I’m thinking about generating a hardware interupt with the encoders to keep track of speed control… Some quick googling gave me this. http://playground.arduino.cc/Code/Interrupts   So on a Uno 2 external interupts  IntO, Int1 are available on Arduino pins 2&3  Ok.. let me check. Dang, pin 2 is apparently being used by the bluetooth module, so that life slightly more complicated.  Although, I have pin change interrupts available.
So I think my next steps are to map out what pins are being used what’s still available, do a little more research on interupts, and PID for DC motor control and try to get a hold of the source that’s currently driving the little bot.  After that or before, I should layout my new base for my bot and print it out.

(Interesting link here on PID stuff. http://web.csulb.edu/~hill/ee444/Lectures/08_PID%20Control.pdf

As well as here: http://www.csimn.com/CSI_pages/PIDforDummies.html

And there is this here…
http://playground.arduino.cc/Code/PIDLibrary

And there is this fun little video which I keep coming back to.
http://makezine.com/2008/11/04/using-a-dc-motor-as-a-ser/

Posted in Uncategorized | Leave a comment

Fooling around with my Musecon robot.

I just got done attending Musecon this year.  I had a great time but scheduling didn’t let attend all three days.  When I  visited Dale and Dwayne of 2DKits and I saw they had some robot kits that they where trying out for the first time.  It’s been something I’ve been thinking about so I got one.

It’s a basic setup but it was pretty easy to get going so that was fun.  Components from China,  the motors and frame where in shipped as one sub-kit and the Arduino uno and Adafruit motor shields where clones as well as the blue tooth adapter came was added by 2dkits.

basic robot setup Bluetooth A

 

 

 

 

 

 

 

The blue tooth module needed a small hack with a voltage divider to get the signal down from 5 to 3.3 volts.

We also hacked up the motor shield clone to add some pins for the blue tooth module..

The motor shield appears to be a clone of the adafruit v1 motor shield with some details that can be found here.

I want to slowly add a bunch of stuff to this system to create a swiffer bot.   Basically, I have alot hardwood floor in my house and having a roomba do it all seems somewhat inefficient.  What I’m thinking about is having this bot, drag a swiffer pad behind it. When it reaches one end of the house it would lift the swiffer slightly to deposit the dirt where it could either be roomba’d or done by hand.

So… I’m thinking I want to 3d print out a nicer fitting base, and add motor encoders(just because) a servo to lift the swiffer to start.   If I don’t get bored or distracted by life, I won’t mind mounting a ping sensor on a another servo..  That’s a ways away though..

For now I just wanted to understand the motor board layout and how it worked.  Basically has 4-H-bridges (via 2 L293D chips) driven by a 74HC595N Serial to parallel output latch.
When reviewing how the latch worked, I ran across this video which I thought really explained it well:

So now, as far as a basic function on a basic motor control using an h-bridge, I thought wiki-pedia explained it pretty clearly. https://en.wikipedia.org/wiki/H_bridge

I’m still a little fuzzy on how the L293D physically works as an H-bridge, but as far as hooking it up, it’s pretty wheel explained here.

Ok.. Now what I’m curious about is how the 74HC595N Serial to parallel output latch relates hooks up to the L293D’s..  I pulled this from here: https://forum.arduino.cc/index.php?topic=211225.0
DK_Multi-MotorShieldSchematic
So basically the 74HC595N is sending is sending out the 2 bits to each motor control 2*4 = 8 bits. Ok. That makes sense.  I’m out of fun time, but I  think I  need to take a closer look to figure this all out as far as how this the arduino pins all get assigned out.
I’m thinking my next step is to figure out how the pins get assigned out and then figure out how the optical encoder works.

Posted in Linistepper, Uncategorized | Leave a comment

Something I’ ve been working on.

This tube only cuts properly if there is a no radius in the Dline.

DlineWithoutRadius

 

Posted in Uncategorized | Leave a comment

Messing around with Pipe bending in freeCAD

I’ve had the need for bending pipe in freeCAD.  I had mentioned this on the IRC as well as the freecad forum.   NormandC  had posted a video on youtube on how to do this using Dwires.

I’m not sure what Navigation styles he’s using. I’ve been studying his video and I’m trying replicate what he’s doing. Basically, I’ve been recording a Macro and trying to figure out underlying python code that’s occurring under the hood. I’ve been having some challenges, replicating his key/mouse clicks with my marblemouse. (Which by the way he uses’s an application called keymon which is sort of cool) https://apps.ubuntu.com/cat/applications/key-mon/ I just downloaded keymon and it’s pretty cool. I think my issue is he’s using a middle mouse click which I haven’t mapped yet on my marble mouse. (I’m think that’s allowing him to enter coordinates into the z-axis. I don’t want to get distracted by remapping my marble mouse so I’m going to work around that for now. I don’t know this for sure but it appears that shift-enter works.)

So. For my first experiment, I to replicated NormandC’s video as best I could. Initially, I was working in inch as my default units and I could get the radius to draw out. When I switched to metric the radius would draw out… Soo… I suspect there might be a critter somewhere in the FreeCAD code with Radius and Inch… Anyway….

First experiment was to create a 50 X 50 X 100 cube and draw a DWire around it.
InitialTestThis is rather amusing .. I was just doing this by eye and it looked liked I was drawing a Dwire to run along the edges of the Cube, but alas it was a delusion errrr..illusion.

So here’s some python code that I got from the macro recording.

# Macro Begin: /home/jonasthomas/FreecadMacros/tubeBendingTest2.FCMacro +++++++++++++++++++++++++++++++++++++++++++++++++
import FreeCAD

#Gui.activateWorkbench(“DraftWorkbench”)
#FreeCAD.DraftWorkingPlane.reset()
#FreeCADGui.Snapper.setGrid()
points=[FreeCAD.Vector(-14.6794960144,-38.9710840913,-16.3023618386),FreeCAD.Vector(15.2086099949,-28.9646975892,-35.2383949065),FreeCAD.Vector(35.6627437396,30.6707607136,-8.51311612836),FreeCAD.Vector(-12.855674845,67.2145578579,61.9107004988),FreeCAD.Vector(-53.5142064538,49.0614256623,84.25678738),FreeCAD.Vector(-72.0758478468,-1.99392771167,62.3064716498)]
Draft.makeWire(points,closed=False,face=False,support=None)
#Gui.activeDocument().activeView().viewTop()
#Gui.activeDocument().activeView().viewTop()
#Gui.activeDocument().activeView().viewTop()
FreeCAD.getDocument(“Unnamed”).getObject(“DWire”).FilletRadius = ‘1 mm’
FreeCAD.getDocument(“Unnamed”).getObject(“DWire”).FilletRadius = ‘2 mm’
FreeCAD.getDocument(“Unnamed”).getObject(“DWire”).FilletRadius = ‘3 mm’
FreeCAD.getDocument(“Unnamed”).getObject(“DWire”).FilletRadius = ‘4 mm’

So it looks like points is a list of freecad vectors which goes into Draft.makeWire. There seems to be some magic that’s occuring prior to the python commands being sent. It seems like there are some fake lines(for a lack of a better term) prior to the python scripts creating the object for real.
From Gui behavior it appears that  Dwire can be appended.. So I found a little documentation on makeWire here

Method.png makeWire ( list or Part.Wire, [closed], [placement], [facemode] )

Description: Creates a DWire object from the given list of vectors or from the given Wire. If closed is True or if first and last points are identical, the wire is closed. If facemode is True (and wire is closed), the wire will appear filled. The current line width and color from the Draft toolbar will be used.

Returns: A new Draft DWire (not a Part Wire).

Ok…… So what’s the difference between a DWire and Part Wire??
A little more info here. http://www.freecadweb.org/wiki/index.php?title=Draft_Wire

Ok.. Experiment#2.. I want to try to record a macro where I create a Dwire, do something else and then try to append points to the Dwire.. Lets see what it looks like.
# Macro Begin: /home/jonasthomas/FreecadMacros/tubetestC.FCMacro +++++++++++++++++++++++++++++++++++++++++++++++++
import FreeCAD
import Part

#FreeCAD.DraftWorkingPlane.reset()
#FreeCADGui.Snapper.setGrid()
points=[FreeCAD.Vector(-2.21234130859,-0.494456797838,0.0),FreeCAD.Vector(-1.5398799181,-0.115844115615,0.0),FreeCAD.Vector(-0.935229718685,-0.890022337437,0.0),FreeCAD.Vector(-0.72049421072,-0.110193289816,0.0)]
Draft.makeWire(points,closed=False,face=False,support=None)
#Gui.activateWorkbench(“PartWorkbench”)
App.ActiveDocument.addObject(“Part::Box”,”Box”)
App.ActiveDocument.ActiveObject.Label = “Cube”
App.ActiveDocument.recompute()
#Gui.SendMsgToActiveView(“ViewFit”)
#Gui.ActiveDocument.setEdit(‘Box’,0)
#Gui.ActiveDocument.setEdit(‘DWire’,0)
#Gui.ActiveDocument.setEdit(‘Box’,0)
#Gui.SendMsgToActiveView(“ViewFit”)
#Gui.getDocument(“Unnamed”).getObject(“Box”).Visibility=False
#Gui.getDocument(“Unnamed”).getObject(“Box”).Visibility=True
#Gui.getDocument(“Unnamed”).getObject(“Box”).Visibility=False
#Gui.getDocument(“Unnamed”).getObject(“DWire”).Visibility=False
#Gui.getDocument(“Unnamed”).getObject(“DWire”).Visibility=True
#Gui.getDocument(“Unnamed”).getObject(“Box”).Visibility=True
#Gui.ActiveDocument.setEdit(‘DWire’,0)
#Gui.ActiveDocument.setEdit(‘DWire’,0)
#Gui.activeDocument().activeView().viewFront()
FreeCAD.getDocument(“Unnamed”).getObject(“DWire”).Closed = False
FreeCAD.getDocument(“Unnamed”).getObject(“DWire”).FilletRadius = ‘2 mm’
FreeCAD.getDocument(“Unnamed”).getObject(“DWire”).FilletRadius = ‘1 mm’
FreeCAD.getDocument(“Unnamed”).getObject(“DWire”).FilletRadius = ‘0 mm’
FreeCAD.getDocument(“Unnamed”).getObject(“DWire”).FilletRadius = ‘0.1 mm’
# Macro End: /home/jonasthomas/FreecadMacros/tubetestC.FCMacro +++++++++++++++++++++++++++++++++++++++++++++++++/

Ok… This is getting more curious.. Apparent the macro recorder is recording everything… The dwire can be appended… Ok.. Now the question is how to do that programatically…
It’s been a year or so since I’ve messed with this stuff… time to google myself 😉http://forum.freecadweb.org/viewtopic.php?f=10&t=3380&start=40#p26606

I think I need to poke around this code for a while. So it looks like draft.Makewire is defined here:
Basically:

 

def makeWire(pointslist,closed=False,placement=None,face=True,support=None):
”’makeWire(pointslist,[closed],[placement]): Creates a Wire object
from the given list of vectors. If closed is True or first
and last points are identical, the wire is closed. If face is
true (and wire is closed), the wire will appear filled. Instead of
a pointslist, you can also pass a Part Wire.”’
import DraftGeomUtils, Part
if not isinstance(pointslist,list):
e = pointslist.Wires[0].Edges
pointslist = Part.Wire(DraftGeomUtils.sortEdges(e))
nlist = []
for v in pointslist.Vertexes:
nlist.append(v.Point)
if DraftGeomUtils.isReallyClosed(pointslist):
closed = True
pointslist = nlist
if len(pointslist) == 0:
print(Invalid input points: ,pointslist)
#print(pointslist)
#print(closed)
if placement: typecheck([(placement,FreeCAD.Placement)], makeWire)
if len(pointslist) == 2: fname = Line
else: fname = DWire
obj = FreeCAD.ActiveDocument.addObject(Part::Part2DObjectPython,fname)
_Wire(obj)
obj.Points = pointslist
obj.Closed = closed
obj.Support = support
#obj.MakeFace = face
if placement: obj.Placement = placement
if gui:
_ViewProviderWire(obj.ViewObject)
formatObject(obj)
select(obj)
FreeCAD.ActiveDocument.recompute()
return obj

Ok..It looks like the wire magic occurs in _wire
which is define  line 4078
So there is some interesting stuff going on with the fillet radius here which takes you to to filletwire

Ok… this is making my head ache.   I think I have my trail of breadcrumbs to follow… What I want to do next if figure out how to append a point to an existing DWire object.

Posted in Uncategorized | Tagged | Leave a comment

Converting Images to printable templates.

I was googling for a way to import a image into freecad and I ran into this page.
http://forum.freecadweb.org/viewtopic.php?f=24&t=5893

This wasn’t what I was looking for but this is pretty cool… Basically takes a 2d gray scale image and converts it to 3d..  Here the link to the Macro FCtexture

So, I thought I give this a try..  My wife had gotten me these for me on for my birthday.

roses

This is just quick experiment… .I need to convert this to grey scale… Which I found away todo  here

RosesGrey.

rosesALooksLikeRosesIf you zoom in on this you can see the roses wth 3d
I guess I can see roses in there.  This requires a bit more research and experimentation.

So I was interested in finding out a little more about bmp file formats.
There’s a nice little blurb on wikipedia as well and some snippet  from stackoverflow

To play around with this macro, I snipped the 4 roses in the upper left hand corner to reduce the computation time.

So I tried a few different settings:
At this point, I’m just playing around on my P-4 laptop with doesn’t seem to have the processing power to handle the image.  Either that or something is locking up..
I it find annoying that I can’t tell if the macro is hangging or just it takes some time to crunch.
One of the things that I thought would be cool if I could take this image and turn it into a solid and 3d print this.. I could see all kinds of interesting things that I can do with this.
At this point, I’ve been trying to brush up on my python,  Refactoring/disceting this code would be a fun excersise for me  Perhaps, read the bitmap data in first before rendering it into freecad…  Also, I think it would be interesting to have this process run in a separate thread and update freecad, so image processing would appear a little more interactive.

(Down the rabbit hole we go.)

GreyRosesB

 

 

 

 
ScreenShotAscreenshotCScreenShotD

 

Posted in Uncategorized | Leave a comment

Some notes on creating a workbench

With all the other stuff I have going on, I don’t know if how much time I have to devote this project but the idea intrigues me and is fun to think about…

If you want to create a workbench in freecad here’s how to get you started.
http://www.freecadweb.org/wiki/index.php?title=Module_Creation

Also there is an assembly2 workbench that looks sort of interesting..

The code behind all this magic is located here… Something definately worth looking at.

Posted in Uncategorized | Leave a comment