Add thickness support

master
Jeremy Hu 2018-03-19 15:58:51 +08:00
parent 24fbff4218
commit f405ac0ede
10 changed files with 163 additions and 52 deletions

View File

@ -24,7 +24,7 @@ int main(int argc, char ** argv)
darkPalette.setColor(QPalette::ButtonText, QColor(239,239,239)); darkPalette.setColor(QPalette::ButtonText, QColor(239,239,239));
darkPalette.setColor(QPalette::BrightText, Qt::red); darkPalette.setColor(QPalette::BrightText, Qt::red);
darkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
darkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); darkPalette.setColor(QPalette::Highlight, QColor(252, 102, 33));
darkPalette.setColor(QPalette::HighlightedText, Qt::black); darkPalette.setColor(QPalette::HighlightedText, Qt::black);
qApp->setPalette(darkPalette); qApp->setPalette(darkPalette);
qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #fc6621; border: 1px solid white; }"); qApp->setStyleSheet("QToolTip { color: #ffffff; background-color: #fc6621; border: 1px solid white; }");

View File

@ -10,6 +10,8 @@
#include <QStackedWidget> #include <QStackedWidget>
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QBuffer> #include <QBuffer>
#include <QFormLayout>
#include <QComboBox>
#include <assert.h> #include <assert.h>
#include "mainwindow.h" #include "mainwindow.h"
#include "skeletonwidget.h" #include "skeletonwidget.h"
@ -52,6 +54,14 @@ MainWindow::MainWindow()
topLayout->addLayout(topButtonsLayout); topLayout->addLayout(topButtonsLayout);
topLayout->addWidget(hrWidget); topLayout->addWidget(hrWidget);
m_edgePropertyWidget = new QWidget;
QFormLayout *formLayout = new QFormLayout;
QComboBox *edgeTypeBox = new QComboBox;
edgeTypeBox->addItem("Spine", "Spine");
edgeTypeBox->addItem("Attach", "Attach");
formLayout->addRow(tr("Edge Type:"), edgeTypeBox);
m_edgePropertyWidget->setLayout(formLayout);
QVBoxLayout *modelRightLayout = new QVBoxLayout; QVBoxLayout *modelRightLayout = new QVBoxLayout;
modelRightLayout->addSpacing(20); modelRightLayout->addSpacing(20);
@ -75,6 +85,10 @@ MainWindow::MainWindow()
modelRightLayout->addWidget(saveModelAsButton); modelRightLayout->addWidget(saveModelAsButton);
saveModelAsButton->hide(); saveModelAsButton->hide();
modelRightLayout->addSpacing(20);
modelRightLayout->addWidget(m_edgePropertyWidget);
modelRightLayout->addStretch(); modelRightLayout->addStretch();
SkeletonWidget *skeletonWidget = new SkeletonWidget(this); SkeletonWidget *skeletonWidget = new SkeletonWidget(this);

View File

@ -22,6 +22,7 @@ private:
QStackedWidget *m_stackedWidget; QStackedWidget *m_stackedWidget;
QString m_saveModelAs; QString m_saveModelAs;
SkeletonWidget *m_skeletonWidget; SkeletonWidget *m_skeletonWidget;
QWidget *m_edgePropertyWidget;
}; };
#endif #endif

View File

@ -8,7 +8,27 @@ SkeletonEditEdgeItem::SkeletonEditEdgeItem(QGraphicsItem *parent) :
m_secondNode(NULL) m_secondNode(NULL)
{ {
setData(0, "edge"); setData(0, "edge");
QPen pen(Theme::skeletonMasterNodeBorderColor); m_checked = false;
updateAppearance();
}
void SkeletonEditEdgeItem::setChecked(bool checked)
{
if (m_checked == checked) {
return;
}
m_checked = checked;
updateAppearance();
}
bool SkeletonEditEdgeItem::checked()
{
return m_checked;
}
void SkeletonEditEdgeItem::updateAppearance()
{
QPen pen(m_checked ? Theme::skeletonMasterNodeBorderHighlightColor : Theme::skeletonMasterNodeBorderColor);
pen.setWidth(Theme::skeletonMasterNodeBorderSize); pen.setWidth(Theme::skeletonMasterNodeBorderSize);
setPen(pen); setPen(pen);
} }

View File

@ -12,9 +12,14 @@ public:
SkeletonEditNodeItem *firstNode(); SkeletonEditNodeItem *firstNode();
SkeletonEditNodeItem *secondNode(); SkeletonEditNodeItem *secondNode();
bool connects(SkeletonEditNodeItem *nodeItem); bool connects(SkeletonEditNodeItem *nodeItem);
void setChecked(bool checked);
bool checked();
private: private:
SkeletonEditNodeItem *m_firstNode; SkeletonEditNodeItem *m_firstNode;
SkeletonEditNodeItem *m_secondNode; SkeletonEditNodeItem *m_secondNode;
bool m_checked;
private:
void updateAppearance();
}; };
#endif #endif

View File

@ -5,6 +5,7 @@
#include <cmath> #include <cmath>
#include <map> #include <map>
#include <vector> #include <vector>
#include <assert.h>
#include "skeletoneditgraphicsview.h" #include "skeletoneditgraphicsview.h"
#include "skeletoneditnodeitem.h" #include "skeletoneditnodeitem.h"
#include "skeletoneditedgeitem.h" #include "skeletoneditedgeitem.h"
@ -21,7 +22,8 @@ SkeletonEditGraphicsView::SkeletonEditGraphicsView(QWidget *parent) :
m_lastHoverNodeItem(NULL), m_lastHoverNodeItem(NULL),
m_lastMousePos(0, 0), m_lastMousePos(0, 0),
m_isMovingNodeItem(false), m_isMovingNodeItem(false),
m_backgroundLoaded(false) m_backgroundLoaded(false),
m_selectedEdgeItem(NULL)
{ {
setScene(new QGraphicsScene()); setScene(new QGraphicsScene());
@ -76,7 +78,7 @@ SkeletonEditNodeItem *SkeletonEditGraphicsView::findNodeItemByPos(QPointF pos)
for (it = list.begin(); it != list.end(); ++it) { for (it = list.begin(); it != list.end(); ++it) {
if ((*it)->data(0).toString() == "node") { if ((*it)->data(0).toString() == "node") {
SkeletonEditNodeItem *nodeItem = static_cast<SkeletonEditNodeItem *>(*it); SkeletonEditNodeItem *nodeItem = static_cast<SkeletonEditNodeItem *>(*it);
if (nodeItem->rect().contains(pos)) { if (nodeItem->shape().contains(pos)) {
return nodeItem; return nodeItem;
} }
} }
@ -84,11 +86,45 @@ SkeletonEditNodeItem *SkeletonEditGraphicsView::findNodeItemByPos(QPointF pos)
return NULL; return NULL;
} }
SkeletonEditEdgeItem *SkeletonEditGraphicsView::findEdgeItemByPos(QPointF pos)
{
QList<QGraphicsItem *>::iterator it;
QList<QGraphicsItem *> list = scene()->items();
for (it = list.begin(); it != list.end(); ++it) {
if ((*it)->data(0).toString() == "edge") {
SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*it);
if (edgeItem->shape().contains(pos)) {
return edgeItem;
}
}
}
return NULL;
}
SkeletonEditEdgeItem *SkeletonEditGraphicsView::findEdgeItemByNodePair(SkeletonEditNodeItem *first,
SkeletonEditNodeItem *second)
{
QList<QGraphicsItem *>::iterator it;
QList<QGraphicsItem *> list = scene()->items();
assert(first != second);
for (it = list.begin(); it != list.end(); ++it) {
if ((*it)->data(0).toString() == "edge") {
SkeletonEditEdgeItem *edgeItem = static_cast<SkeletonEditEdgeItem *>(*it);
if ((edgeItem->firstNode() == first || edgeItem->secondNode() == first) &&
(edgeItem->firstNode() == second || edgeItem->secondNode() == second)) {
return edgeItem;
}
}
}
return NULL;
}
void SkeletonEditGraphicsView::mousePressEvent(QMouseEvent *event) void SkeletonEditGraphicsView::mousePressEvent(QMouseEvent *event)
{ {
QWidget::mousePressEvent(event); QWidget::mousePressEvent(event);
if (!m_backgroundLoaded) if (!m_backgroundLoaded)
return; return;
QPointF pos = mapToScene(event->pos());
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
if (!m_inAddNodeMode) { if (!m_inAddNodeMode) {
if (m_lastHoverNodeItem) { if (m_lastHoverNodeItem) {
@ -99,9 +135,27 @@ void SkeletonEditGraphicsView::mousePressEvent(QMouseEvent *event)
setNextStartNodeItem(NULL); setNextStartNodeItem(NULL);
} }
} }
SkeletonEditEdgeItem *edgeItem = findEdgeItemByPos(pos);
if (edgeItem) {
if (m_selectedEdgeItem != edgeItem) {
if (m_selectedEdgeItem) {
m_selectedEdgeItem->setChecked(false);
m_selectedEdgeItem = NULL;
}
edgeItem->setChecked(true);
m_selectedEdgeItem = edgeItem;
emit edgeCheckStateChanged();
}
} else {
if (m_selectedEdgeItem) {
m_selectedEdgeItem->setChecked(false);
m_selectedEdgeItem = NULL;
emit edgeCheckStateChanged();
}
}
} }
} }
m_lastMousePos = mapToScene(event->pos()); m_lastMousePos = pos;
} }
void SkeletonEditGraphicsView::mouseDoubleClickEvent(QMouseEvent *event) void SkeletonEditGraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
@ -195,29 +249,40 @@ void SkeletonEditGraphicsView::mouseReleaseEvent(QMouseEvent *event)
return; return;
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
if (m_inAddNodeMode) { if (m_inAddNodeMode) {
SkeletonEditNodeItem *masterNode = new SkeletonEditNodeItem(m_pendingNodeItem->rect()); if (m_lastHoverNodeItem && m_nextStartNodeItem && m_lastHoverNodeItem != m_nextStartNodeItem) {
scene()->addItem(masterNode); if (!findEdgeItemByNodePair(m_lastHoverNodeItem->master(), m_nextStartNodeItem)) {
QRectF slaveRect = m_pendingNodeItem->rect(); SkeletonEditEdgeItem *newEdge = new SkeletonEditEdgeItem();
float x = m_pendingNodeItem->x() + m_pendingNodeItem->rect().width() / 2; newEdge->setNodes(m_nextStartNodeItem, m_lastHoverNodeItem->master());
slaveRect.translate(findXForSlave(x) - x, 0); scene()->addItem(newEdge);
SkeletonEditNodeItem *slaveNode = new SkeletonEditNodeItem(slaveRect); emit nodesChanged();
scene()->addItem(slaveNode); }
masterNode->setSlave(slaveNode);
slaveNode->setMaster(masterNode);
if (m_nextStartNodeItem) {
SkeletonEditEdgeItem *newEdge = new SkeletonEditEdgeItem();
newEdge->setNodes(masterNode, m_nextStartNodeItem);
scene()->addItem(newEdge);
masterNode->setGroup(m_nextStartNodeItem->group());
slaveNode->setGroup(masterNode->group());
} else { } else {
QGraphicsItemGroup *group = new QGraphicsItemGroup(); float newNodeX = m_pendingNodeItem->x();
scene()->addItem(group); QRectF newNodeRect = m_pendingNodeItem->rect();
masterNode->setGroup(group); SkeletonEditNodeItem *masterNode = new SkeletonEditNodeItem(newNodeRect);
slaveNode->setGroup(masterNode->group()); scene()->addItem(masterNode);
QRectF slaveRect = newNodeRect;
float x = newNodeX + newNodeRect.width() / 2;
slaveRect.translate(findXForSlave(x) - x, 0);
SkeletonEditNodeItem *slaveNode = new SkeletonEditNodeItem(slaveRect);
scene()->addItem(slaveNode);
masterNode->setSlave(slaveNode);
slaveNode->setMaster(masterNode);
if (m_nextStartNodeItem) {
SkeletonEditEdgeItem *newEdge = new SkeletonEditEdgeItem();
newEdge->setNodes(masterNode, m_nextStartNodeItem);
scene()->addItem(newEdge);
masterNode->setGroup(m_nextStartNodeItem->group());
slaveNode->setGroup(masterNode->group());
} else {
QGraphicsItemGroup *group = new QGraphicsItemGroup();
scene()->addItem(group);
masterNode->setGroup(group);
slaveNode->setGroup(masterNode->group());
}
setNextStartNodeItem(masterNode);
emit nodesChanged();
} }
setNextStartNodeItem(masterNode);
emit nodesChanged();
} }
m_isMovingNodeItem = false; m_isMovingNodeItem = false;
} }
@ -260,21 +325,14 @@ void SkeletonEditGraphicsView::mouseMoveEvent(QMouseEvent *event)
moveTo.y() + m_pendingNodeItem->rect().height() / 2))); moveTo.y() + m_pendingNodeItem->rect().height() / 2)));
} }
if (!m_isMovingNodeItem) { if (!m_isMovingNodeItem) {
if (!m_inAddNodeMode) { SkeletonEditNodeItem *hoverNodeItem = findNodeItemByPos(pos);
SkeletonEditNodeItem *hoverNodeItem = findNodeItemByPos(pos); if (hoverNodeItem) {
if (hoverNodeItem) { hoverNodeItem->setHighlighted(true);
hoverNodeItem->setHighlighted(true); }
} if (hoverNodeItem != m_lastHoverNodeItem) {
if (hoverNodeItem != m_lastHoverNodeItem) { if (m_lastHoverNodeItem)
if (m_lastHoverNodeItem)
m_lastHoverNodeItem->setHighlighted(false);
m_lastHoverNodeItem = hoverNodeItem;
}
} else {
if (m_lastHoverNodeItem) {
m_lastHoverNodeItem->setHighlighted(false); m_lastHoverNodeItem->setHighlighted(false);
m_lastHoverNodeItem = NULL; m_lastHoverNodeItem = hoverNodeItem;
}
} }
} }
QPointF curMousePos = pos; QPointF curMousePos = pos;
@ -363,10 +421,8 @@ void SkeletonEditGraphicsView::wheelEvent(QWheelEvent *event)
delta = delta < 0 ? -1.0 : 1.0; delta = delta < 0 ? -1.0 : 1.0;
AddItemRadius(m_pendingNodeItem, delta); AddItemRadius(m_pendingNodeItem, delta);
if (!m_inAddNodeMode && m_lastHoverNodeItem) { if (!m_inAddNodeMode && m_lastHoverNodeItem) {
if (canAddItemRadius(m_lastHoverNodeItem, delta) && if (canAddItemRadius(m_lastHoverNodeItem, delta)) {
canAddItemRadius(m_lastHoverNodeItem->pair(), delta)) {
AddItemRadius(m_lastHoverNodeItem, delta); AddItemRadius(m_lastHoverNodeItem, delta);
AddItemRadius(m_lastHoverNodeItem->pair(), delta);
} }
emit nodesChanged(); emit nodesChanged();
} }

View File

@ -15,6 +15,7 @@ signals:
void sizeChanged(); void sizeChanged();
void nodesChanged(); void nodesChanged();
void changeTurnaroundTriggered(); void changeTurnaroundTriggered();
void edgeCheckStateChanged();
public slots: public slots:
void turnOffAddNodeMode(); void turnOffAddNodeMode();
void turnOnAddNodeMode(); void turnOnAddNodeMode();
@ -45,10 +46,14 @@ private:
QPointF m_lastMousePos; QPointF m_lastMousePos;
bool m_isMovingNodeItem; bool m_isMovingNodeItem;
bool m_backgroundLoaded; bool m_backgroundLoaded;
SkeletonEditEdgeItem *m_selectedEdgeItem;
private: private:
void toggleAddNodeMode(); void toggleAddNodeMode();
void applyAddNodeMode(); void applyAddNodeMode();
SkeletonEditNodeItem *findNodeItemByPos(QPointF pos); SkeletonEditNodeItem *findNodeItemByPos(QPointF pos);
SkeletonEditEdgeItem *findEdgeItemByPos(QPointF pos);
SkeletonEditEdgeItem *findEdgeItemByNodePair(SkeletonEditNodeItem *first,
SkeletonEditNodeItem *second);
void setNextStartNodeItem(SkeletonEditNodeItem *item); void setNextStartNodeItem(SkeletonEditNodeItem *item);
float findXForSlave(float x); float findXForSlave(float x);
bool canNodeItemMoveTo(SkeletonEditNodeItem *item, QPointF moveTo); bool canNodeItemMoveTo(SkeletonEditNodeItem *item, QPointF moveTo);

View File

@ -4,13 +4,20 @@
#include "skeletoneditedgeitem.h" #include "skeletoneditedgeitem.h"
#include <vector> #include <vector>
#define USE_CARVE 1
#if USE_CARVE == 1
#if defined(HAVE_CONFIG_H) #if defined(HAVE_CONFIG_H)
# include <carve_config.h> # include <carve_config.h>
#endif #endif
#include <carve/carve.hpp> #include <carve/carve.hpp>
#include <carve/csg.hpp> #include <carve/csg.hpp>
#include <carve/input.hpp> #include <carve/input.hpp>
#endif
#define MAX_VERTICES_PER_FACE 100
#if USE_CARVE == 0
// Polygon_mesh_processing/corefinement_mesh_union.cpp // Polygon_mesh_processing/corefinement_mesh_union.cpp
// https://doc.cgal.org/latest/Polygon_mesh_processing/Polygon_mesh_processing_2corefinement_mesh_union_8cpp-example.html#a2 // https://doc.cgal.org/latest/Polygon_mesh_processing/Polygon_mesh_processing_2corefinement_mesh_union_8cpp-example.html#a2
// https://doc.cgal.org/latest/Polygon_mesh_processing/Polygon_mesh_processing_2triangulate_faces_example_8cpp-example.html // https://doc.cgal.org/latest/Polygon_mesh_processing/Polygon_mesh_processing_2triangulate_faces_example_8cpp-example.html
@ -20,14 +27,10 @@
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h> #include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
#include <CGAL/boost/graph/iterator.h> #include <CGAL/boost/graph/iterator.h>
// Modified from https://wiki.qt.io/QThreads_general_usage
typedef CGAL::Exact_predicates_inexact_constructions_kernel K; typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> CgalMesh; typedef CGAL::Surface_mesh<K::Point_3> CgalMesh;
namespace PMP = CGAL::Polygon_mesh_processing; namespace PMP = CGAL::Polygon_mesh_processing;
#define MAX_VERTICES_PER_FACE 100
CgalMesh *makeCgalMeshFromMeshlite(void *meshlite, int meshId) CgalMesh *makeCgalMeshFromMeshlite(void *meshlite, int meshId)
{ {
CgalMesh *mesh = new CgalMesh; CgalMesh *mesh = new CgalMesh;
@ -115,6 +118,10 @@ CgalMesh *unionCgalMeshs(CgalMesh *first, CgalMesh *second)
return mesh; return mesh;
} }
#endif
#if USE_CARVE == 1
carve::poly::Polyhedron *makeCarveMeshFromMeshlite(void *meshlite, int meshId) carve::poly::Polyhedron *makeCarveMeshFromMeshlite(void *meshlite, int meshId)
{ {
carve::input::PolyhedronData data; carve::input::PolyhedronData data;
@ -187,6 +194,8 @@ carve::poly::Polyhedron *unionCarveMeshs(carve::poly::Polyhedron *first,
return new carve::poly::Polyhedron(*result); return new carve::poly::Polyhedron(*result);
} }
#endif
struct NodeItemInfo struct NodeItemInfo
{ {
int index; int index;
@ -231,6 +240,7 @@ SkeletonToMesh::SkeletonToMesh(SkeletonEditGraphicsView *graphicsView) :
node.originZ = nodeItem->slave()->origin().x(); node.originZ = nodeItem->slave()->origin().x();
node.bmeshNodeId = -1; node.bmeshNodeId = -1;
node.radius = nodeItem->radius(); node.radius = nodeItem->radius();
node.thickness = nodeItem->slave()->radius();
info.index = skeletonGroup->nodes.size(); info.index = skeletonGroup->nodes.size();
info.neighborCount = 1; info.neighborCount = 1;
@ -267,8 +277,6 @@ Mesh *SkeletonToMesh::takeResultMesh()
return mesh; return mesh;
} }
#define USE_CARVE 1
#if USE_CARVE #if USE_CARVE
#define ExternalMesh carve::poly::Polyhedron #define ExternalMesh carve::poly::Polyhedron
#define makeExternalMeshFromMeshlite makeCarveMeshFromMeshlite #define makeExternalMeshFromMeshlite makeCarveMeshFromMeshlite
@ -334,7 +342,8 @@ void SkeletonToMesh::process()
float y = (node->originY - top - height / 2) / height; float y = (node->originY - top - height / 2) / height;
float z = (node->originZ - zLeft - zWidth / 2) / height; float z = (node->originZ - zLeft - zWidth / 2) / height;
float r = node->radius / height; float r = node->radius / height;
node->bmeshNodeId = meshlite_bmesh_add_node(context, group->bmeshId, x, y, z, r); float t = node->thickness / height;
node->bmeshNodeId = meshlite_bmesh_add_node(context, group->bmeshId, x, y, z, r, t);
} }
for (size_t j = 0; j < group->edges.size(); j++) { for (size_t j = 0; j < group->edges.size(); j++) {
SkeletonNode *firstNode = &group->nodes[group->edges[j].firstNode]; SkeletonNode *firstNode = &group->nodes[group->edges[j].firstNode];

View File

@ -13,6 +13,7 @@ struct SkeletonNode
float originY; float originY;
float originZ; float originZ;
float radius; float radius;
float thickness;
int bmeshNodeId; int bmeshNodeId;
}; };

View File

@ -7,11 +7,11 @@
QColor Theme::skeletonMasterNodeBorderColor = QColor(0xfc, 0x66, 0x21, 128); QColor Theme::skeletonMasterNodeBorderColor = QColor(0xfc, 0x66, 0x21, 128);
QColor Theme::skeletonMasterNodeBorderHighlightColor = QColor(0xfc, 0x66, 0x21); QColor Theme::skeletonMasterNodeBorderHighlightColor = QColor(0xfc, 0x66, 0x21);
QColor Theme::skeletonMasterNodeFillColor = QColor(0xfc, 0x66, 0x21, 50); QColor Theme::skeletonMasterNodeFillColor = QColor(0xfc, 0x66, 0x21, 50);
int Theme::skeletonMasterNodeBorderSize = 2; int Theme::skeletonMasterNodeBorderSize = 7;
QColor Theme::skeletonSlaveNodeBorderColor = QColor(0xcc, 0xcc, 0xcc, 64); QColor Theme::skeletonSlaveNodeBorderColor = QColor(0xcc, 0xcc, 0xcc, 64);
QColor Theme::skeletonSlaveNodeBorderHighlightColor = QColor(0xcc, 0xcc, 0xcc); QColor Theme::skeletonSlaveNodeBorderHighlightColor = QColor(0xcc, 0xcc, 0xcc);
QColor Theme::skeletonSlaveNodeFillColor = QColor(0xcc, 0xcc, 0xcc, 25); QColor Theme::skeletonSlaveNodeFillColor = QColor(0xcc, 0xcc, 0xcc, 25);
int Theme::skeletonSlaveNodeBorderSize = 2; int Theme::skeletonSlaveNodeBorderSize = 7;
QString Theme::tabButtonSelectedStylesheet = "QPushButton { color: #efefef; background-color: #fc6621; border: 0px; padding-top: 2px; padding-bottom: 2px; padding-left: 25px; padding-right: 25px;}"; QString Theme::tabButtonSelectedStylesheet = "QPushButton { color: #efefef; background-color: #fc6621; border: 0px; padding-top: 2px; padding-bottom: 2px; padding-left: 25px; padding-right: 25px;}";
QString Theme::tabButtonStylesheet = "QPushButton { color: #efefef; background-color: #353535; border: 0px; padding-top: 2px; padding-bottom: 2px; padding-left: 25px; padding-right: 25px;}"; QString Theme::tabButtonStylesheet = "QPushButton { color: #efefef; background-color: #353535; border: 0px; padding-top: 2px; padding-bottom: 2px; padding-left: 25px; padding-right: 25px;}";