Subclass CutterDockWidget in Python Plugin

Refactor CutterPlugin::setupInterface() and fix C++ sample
This commit is contained in:
Florian Märkl 2019-02-07 17:19:05 +01:00
parent 23115d7b1c
commit 280e10d154
18 changed files with 133 additions and 120 deletions

View File

@ -97,17 +97,11 @@ or, if you don't want to build with Python at all, use -DCUTTER_ENABLE_PYTHON=OF
endif()
include_directories(${SHIBOKEN_INCLUDE_DIR})
include_directories(${PYSIDE_INCLUDE_DIR})
include_directories(${PYSIDE_INCLUDE_DIR}/QtCore)
include_directories(${PYSIDE_INCLUDE_DIR} ${PYSIDE_INCLUDE_DIR}/QtCore ${PYSIDE_INCLUDE_DIR}/QtGui ${PYSIDE_INCLUDE_DIR}/QtWidgets)
endif()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_definitions(-Wall -Wextra)
endif()
message(STATUS "")
message(STATUS "Building Cutter version ${CUTTER_VERSION_FULL}")
@ -126,21 +120,32 @@ qmake_configure_file("${CMAKE_CURRENT_SOURCE_DIR}/CutterConfig.h.in"
set(BINDINGS_SOURCE
"${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/cutterbindings_module_wrapper.cpp"
#"${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/cutterdockwidget_wrapper.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/cuttercore_wrapper.cpp")
"${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/cuttercore_wrapper.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/configuration_wrapper.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/cutterdockwidget_wrapper.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings/mainwindow_wrapper.cpp")
set_property(SOURCE ${BINDINGS_SOURCE} PROPERTY SKIP_AUTOGEN ON)
include_directories("${CMAKE_CURRENT_BINARY_DIR}/bindings/CutterBindings")
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/widgets")
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/common")
add_custom_command(OUTPUT ${BINDINGS_SOURCE}
COMMAND shiboken2 --project-file=${CMAKE_CURRENT_BINARY_DIR}/bindings/bindings.txt
#DEPENDS ${sample_TYPESYSTEM} ${CMAKE_CURRENT_SOURCE_DIR}/global.h shiboken2
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND shiboken2 --project-file="${CMAKE_CURRENT_BINARY_DIR}/bindings/bindings.txt"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/bindings/bindings.xml"
IMPLICIT_DEPENDS CXX "${CMAKE_CURRENT_SOURCE_DIR}/bindings/bindings.h"
COMMENT "Generating Python bindings with shiboken2")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_definitions(-Wall -Wextra)
set_source_files_properties(${BINDINGS_SOURCE} PROPERTIES COMPILE_FLAGS -w)
endif()
add_executable(Cutter ${UI_FILES} ${QRC_FILES} ${SOURCE_FILES} ${HEADER_FILES} ${BINDINGS_SOURCE})
target_link_libraries(Cutter Qt5::Core Qt5::Widgets Qt5::Gui Qt5::Svg Qt5::Network)

View File

@ -278,10 +278,7 @@ void MainWindow::initUI()
/* Setup plugins interfaces */
QList<CutterPlugin *> plugins = core->getCutterPlugins();
for (auto plugin : plugins) {
QDockWidget *pluginDock = plugin->setupInterface(this);
if (pluginDock) {
tabifyDockWidget(dashboardDock, pluginDock);
}
plugin->setupInterface(this);
}
}
@ -383,6 +380,15 @@ void MainWindow::addExtraWidget(QDockWidget *extraDock)
restoreExtraDock.restoreWidth(extraDock->widget());
}
void MainWindow::addPluginDockWidget(QDockWidget *dockWidget, QAction *action)
{
addDockWidget(Qt::TopDockWidgetArea, dockWidget);
addDockWidgetAction(dockWidget, action);
ui->menuWindows->addAction(action);
tabifyDockWidget(dashboardDock, dockWidget);
updateDockActionChecked(action);
}
void MainWindow::openNewFile(InitialOptions options, bool skipOptionsDialog)
{
setFilename(options.filename);

View File

@ -103,6 +103,8 @@ public:
void addDockWidgetAction(QDockWidget *dockWidget, QAction *action);
void addExtraWidget(QDockWidget *extraDock);
void addPluginDockWidget(QDockWidget *dockWidget, QAction *action);
void updateDockActionChecked(QAction * action);
QString getFilename() const

View File

@ -2,7 +2,9 @@
#ifndef CUTTER_BINDINGS_H
#define CUTTER_BINDINGS_H
//#include "../widgets/CutterDockWidget.h"
#include "../Cutter.h"
#include "../common/Configuration.h"
#include "../MainWindow.h"
#include "../widgets/CutterDockWidget.h"
#endif //CUTTER_BINDINGS_H

View File

@ -1,8 +1,11 @@
<?xml version="1.0"?>
<typesystem package="CutterBindings">
<load-typesystem name="typesystem_core.xml" generate="no" />
<load-typesystem name="typesystem_gui.xml" generate="no" />
<load-typesystem name="typesystem_widgets.xml" generate="no" />
<primitive-type name="bool"/>
<primitive-type name="std::string"/>
<!--<object-type name="CutterDockWidget" />-->
<object-type name="CutterCore" />
<object-type name="Configuration" />
<object-type name="MainWindow" />
<object-type name="CutterDockWidget" />
</typesystem>

View File

@ -12,7 +12,7 @@ class CutterPlugin
public:
virtual ~CutterPlugin() {}
virtual void setupPlugin(CutterCore *core) = 0;
virtual QDockWidget *setupInterface(MainWindow *main, QAction *action = nullptr) = 0;
virtual void setupInterface(MainWindow *main) = 0;
QString name;
QString description;
@ -21,7 +21,6 @@ public:
protected:
CutterCore *core;
CutterDockWidget *dockable;
};
#define CutterPlugin_iid "org.radare.cutter.plugins.CutterPlugin"

View File

@ -93,10 +93,9 @@ QString CutterPythonPlugin::getAttributeFromPython(const char *attribute)
return result;
}
QDockWidget *CutterPythonPlugin::setupInterface(MainWindow *main, QAction *action)
void CutterPythonPlugin::setupInterface(MainWindow *main)
{
Q_UNUSED(main)
Q_UNUSED(action)
PyObject *pWidget = nullptr;
Python()->restoreThread();
@ -106,23 +105,8 @@ QDockWidget *CutterPythonPlugin::setupInterface(MainWindow *main, QAction *actio
qWarning() << "Error in setupInterface().";
PyErr_Print();
Python()->saveThread();
return nullptr;
}
if (!PyLong_Check(pWidget)) {
qWarning() << "Value returned by setupInterface() is not PyLong.";
Python()->saveThread();
return nullptr;
}
auto dockWidget = reinterpret_cast<QDockWidget *>(PyLong_AsLong(pWidget));
if (!dockWidget) {
qWarning() << "Cannot instantiate QDockWidget.";
Python()->saveThread();
return nullptr;
return;
}
Python()->saveThread();
return dockWidget;
}

View File

@ -10,7 +10,7 @@ public:
CutterPythonPlugin(PyObject* pluginModule);
~CutterPythonPlugin();
void setupPlugin(CutterCore *core);
QDockWidget *setupInterface(MainWindow *main, QAction *action);
void setupInterface(MainWindow *main);
private:
PyObject *pluginModule = nullptr;

View File

@ -1,13 +0,0 @@
HEADERS += CutterSamplePlugin.h CutterPlugin.h
INCLUDEPATH += ../
SOURCES += CutterSamplePlugin.cpp
# Needed for r_core include TODO cross platform
unix:exists(/usr/include/libr) {
INCLUDEPATH += /usr/include/libr
}
TEMPLATE = lib
CONFIG += plugin
QT += widgets
TARGET = PluginSample

View File

@ -1,51 +0,0 @@
import cutter
from cutter_plugin import CutterPlugin
from PySide2 import QtWidgets
from PySide2.QtCore import QObject, SIGNAL, Qt
from PySide2.QtGui import QFont
import CutterBindings
class CutterSamplePlugin(CutterPlugin):
name = "SamplePlugin"
description = "A sample plugin written in python."
version = "1.0"
author = "xarkes and thestr4ng3r :-P"
def setupInterface(self):
super().setupInterface()
# Create dock widget and content widget
dock_widget = QtWidgets.QDockWidget(self.main)
dock_widget.setObjectName("FancyDockWidgetFromCoolPlugin")
dock_widget.setWindowTitle("Test Widget")
content = QtWidgets.QWidget()
dock_widget.setWidget(content)
# Create layout and label
layout = QtWidgets.QVBoxLayout(dock_widget)
content.setLayout(layout)
self.text = QtWidgets.QLabel(content)
self.text.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
layout.addWidget(self.text)
button = QtWidgets.QPushButton(content)
button.setText("Want a fortune?")
button.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
button.setMaximumHeight(50)
button.setMaximumWidth(200)
layout.addWidget(button)
layout.setAlignment(button, Qt.AlignHCenter)
QObject.connect(CutterBindings.CutterCore.getInstance(), SIGNAL("seekChanged(RVA)"), self.generate_fortune)
QObject.connect(button, SIGNAL("clicked()"), self.generate_fortune)
return self.makeCppPointer(dock_widget)
def generate_fortune(self):
res = cutter.cmd("?E `fo`")
self.text.setText(res)
# Instantiate our plugin
plugin = CutterSamplePlugin()

View File

@ -1,10 +1,12 @@
#include <QLabel>
#include <QHBoxLayout>
#include <QPushButton>
#include <QAction>
#include "CutterSamplePlugin.h"
#include "common/TempConfig.h"
#include "common/Configuration.h"
#include "MainWindow.h"
void CutterSamplePlugin::setupPlugin(CutterCore *core)
{
@ -15,22 +17,23 @@ void CutterSamplePlugin::setupPlugin(CutterCore *core)
this->author = "xarkes";
}
CutterDockWidget* CutterSamplePlugin::setupInterface(MainWindow *main, QAction* actions)
void CutterSamplePlugin::setupInterface(MainWindow *main)
{
// Instantiate dock widget
dockable = new CutterSamplePluginWidget(main, actions);
return dockable;
QAction *action = new QAction("Sample C++ Plugin", main);
action->setCheckable(true);
CutterSamplePluginWidget *widget = new CutterSamplePluginWidget(main, action);
main->addPluginDockWidget(widget, action);
}
CutterSamplePluginWidget::CutterSamplePluginWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action)
{
this->setObjectName("CutterSamplePluginWidget");
this->setWindowTitle("Sample Plugin");
this->setWindowTitle("Sample C++ Plugin");
QWidget *content = new QWidget();
this->setWidget(content);
QVBoxLayout *layout = new QVBoxLayout(this);
QVBoxLayout *layout = new QVBoxLayout(content);
content->setLayout(layout);
text = new QLabel(content);
text->setFont(Config()->getFont());
@ -63,6 +66,7 @@ void CutterSamplePluginWidget::on_seekChanged(RVA addr)
void CutterSamplePluginWidget::on_buttonClicked()
{
QString res = Core()->cmd("?E `fo`");
QString fortune = Core()->cmd("fo").replace("\n", "");
QString res = Core()->cmdRaw("?E " + fortune);
text->setText(res);
}

View File

@ -13,7 +13,7 @@ class CutterSamplePlugin : public QObject, CutterPlugin
public:
void setupPlugin(CutterCore *core) override;
CutterDockWidget* setupInterface(MainWindow *main, QAction *action = nullptr) override;
void setupInterface(MainWindow *main) override;
};
class CutterSamplePluginWidget : public CutterDockWidget

View File

@ -0,0 +1,10 @@
HEADERS += ../CutterSamplePlugin.h ../CutterPlugin.h
INCLUDEPATH += ../ ../../
SOURCES += CutterSamplePlugin.cpp
QMAKE_CXXFLAGS += $$system("pkg-config --cflags r_core")
TEMPLATE = lib
CONFIG += plugin
QT += widgets
TARGET = PluginSample

View File

@ -0,0 +1,62 @@
import cutter
from cutter_plugin import CutterPlugin
import CutterBindings
from PySide2.QtCore import QObject, SIGNAL, Qt
from PySide2.QtWidgets import QAction, QVBoxLayout, QLabel, QWidget, QSizePolicy, QPushButton
class FortuneWidget(CutterBindings.CutterDockWidget):
def __init__(self, main, action):
super(FortuneWidget, self).__init__(main, action)
self.setObjectName("FancyDockWidgetFromCoolPlugin")
self.setWindowTitle("Sample Python Plugin")
content = QWidget()
self.setWidget(content)
# Create layout and label
layout = QVBoxLayout(content)
content.setLayout(layout)
self.text = QLabel(content)
self.text.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
self.text.setFont(CutterBindings.Configuration.instance().getFont())
layout.addWidget(self.text)
button = QPushButton(content)
button.setText("Want a fortune?")
button.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
button.setMaximumHeight(50)
button.setMaximumWidth(200)
layout.addWidget(button)
layout.setAlignment(button, Qt.AlignHCenter)
QObject.connect(CutterBindings.CutterCore.getInstance(), SIGNAL("seekChanged(RVA)"), self.generate_fortune)
QObject.connect(button, SIGNAL("clicked()"), self.generate_fortune)
self.show()
def generate_fortune(self):
fortune = cutter.cmd("fo").replace("\n", "")
res = CutterBindings.CutterCore.getInstance().cmdRaw(f"?E {fortune}")
self.text.setText(res)
class CutterSamplePlugin(CutterPlugin):
name = "SamplePlugin"
description = "A sample plugin written in python."
version = "1.0"
author = "xarkes and thestr4ng3r :-P"
def setupInterface(self):
super().setupInterface()
self.action = QAction("Sample Python Plugin", self.main)
self.action.setCheckable(True)
self.widget = FortuneWidget(self.main, self.action) # we MUST keep a reference to this!
self.main.addPluginDockWidget(self.widget, self.action)
# Instantiate our plugin
plugin = CutterSamplePlugin()

View File

@ -16,10 +16,3 @@ class CutterPlugin(object):
if widget.objectName() == "MainWindow":
self.main = widget
break
def makeCppPointer(self, widget):
ptr = shiboken2.getCppPointer(widget)[0]
return ptr

View File

@ -8,6 +8,7 @@
#include <QList>
#include <QFileInfo>
#include <QToolBar>
#include <QToolButton>
DebugActions::DebugActions(QToolBar *toolBar, MainWindow *main) :
QObject(main),

View File

@ -4,6 +4,7 @@
#include <QDebug>
#include <QTextEdit>
#include <QMouseEvent>
#include <QAction>
#include <array>
#include <memory>
@ -19,7 +20,9 @@
#include "Dashboard.h"
#include "ui_HexdumpWidget.h"
namespace Ui {
class HexdumpWidget;
}
class RefreshDeferrer;

View File

@ -4,6 +4,9 @@
#include "MainWindow.h"
#include <QLabel>
#include <QLineEdit>
RegistersWidget::RegistersWidget(MainWindow *main, QAction *action) :
CutterDockWidget(main, action),
ui(new Ui::RegistersWidget)