mirror of
https://github.com/yse/easy_profiler.git
synced 2025-01-14 00:27:55 +08:00
(GUI) Added flag for enabling zero length blocks on diagram (if NOT enabled then such blocks would be resized to minimum length which is 250 ns. Otherwise you probably will not see blocks with zero length on diagram, but such blocks are still available at hierarchy window)
This commit is contained in:
parent
53fd4df281
commit
a7ac056021
@ -166,6 +166,9 @@ void EasyBackgroundItem::paint(QPainter* _painter, const QStyleOptionGraphicsIte
|
||||
if (dh > 0)
|
||||
rect.setHeight(rect.height() - dh);
|
||||
|
||||
if (rect.top() < 0)
|
||||
rect.setTop(0);
|
||||
|
||||
_painter->drawRect(rect);
|
||||
}
|
||||
}
|
||||
@ -559,10 +562,10 @@ qreal EasyGraphicsView::setTree(EasyGraphicsItem* _item, ::profiler::block_index
|
||||
// xbegin -= dt;
|
||||
//}
|
||||
|
||||
if (duration < MIN_DURATION)
|
||||
{
|
||||
duration = MIN_DURATION;
|
||||
}
|
||||
//if (duration < MIN_DURATION)
|
||||
//{
|
||||
// duration = MIN_DURATION;
|
||||
//}
|
||||
|
||||
auto i = _item->addItem(level);
|
||||
auto& b = _item->getItem(level, i);
|
||||
@ -868,7 +871,7 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
|
||||
}
|
||||
}
|
||||
|
||||
const ::profiler_gui::EasyBlockItem* selectedBlock = nullptr;
|
||||
const ::profiler_gui::EasyBlock* selectedBlock = nullptr;
|
||||
const auto previouslySelectedBlock = EASY_GLOBALS.selected_block;
|
||||
if (m_mouseButtons & Qt::LeftButton)
|
||||
{
|
||||
@ -900,12 +903,13 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
|
||||
// Try to select one of item blocks
|
||||
for (auto item : m_items)
|
||||
{
|
||||
auto block = item->intersect(mouseClickPos);
|
||||
::profiler::block_index_t i = ~0U;
|
||||
auto block = item->intersect(mouseClickPos, i);
|
||||
if (block)
|
||||
{
|
||||
changedSelectedItem = true;
|
||||
selectedBlock = block;
|
||||
EASY_GLOBALS.selected_block = block->block;
|
||||
EASY_GLOBALS.selected_block = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -934,7 +938,7 @@ void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
|
||||
if (changedSelectedItem)
|
||||
{
|
||||
m_bUpdatingRect = true;
|
||||
if (selectedBlock != nullptr && previouslySelectedBlock == EASY_GLOBALS.selected_block && !blocksTree(selectedBlock->block).children.empty())
|
||||
if (selectedBlock != nullptr && previouslySelectedBlock == EASY_GLOBALS.selected_block && !selectedBlock->tree.children.empty())
|
||||
{
|
||||
EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded = !EASY_GLOBALS.gui_blocks[previouslySelectedBlock].expanded;
|
||||
emit EASY_GLOBALS.events.itemsExpandStateChanged();
|
||||
@ -1293,30 +1297,44 @@ void EasyGraphicsView::onIdleTimeout()
|
||||
// Try to select one of context switches or items
|
||||
for (auto item : m_items)
|
||||
{
|
||||
auto block = item->intersect(pos);
|
||||
::profiler::block_index_t i = ~0U;
|
||||
auto block = item->intersect(pos, i);
|
||||
if (block)
|
||||
{
|
||||
const auto& itemBlock = blocksTree(block->block);
|
||||
auto name = *itemBlock.node->name() != 0 ? itemBlock.node->name() : easyDescriptor(itemBlock.node->id()).name();
|
||||
const auto& itemBlock = block->tree;
|
||||
const auto& itemDesc = easyDescriptor(itemBlock.node->id());
|
||||
auto name = *itemBlock.node->name() != 0 ? itemBlock.node->name() : itemDesc.name();
|
||||
|
||||
auto widget = new QWidget();
|
||||
auto lay = new QFormLayout(widget);
|
||||
lay->setLabelAlignment(Qt::AlignRight);
|
||||
lay->addRow("Name:", new QLabel(name));
|
||||
lay->addRow("Duration:", new QLabel(::profiler_gui::timeStringReal(PROF_MICROSECONDS(itemBlock.node->duration()), 3)));
|
||||
if (itemDesc.type() == ::profiler::BLOCK_TYPE_BLOCK)
|
||||
{
|
||||
lay->addRow("Block:", new QLabel(name));
|
||||
lay->addRow("Duration:", new QLabel(::profiler_gui::timeStringReal(PROF_MICROSECONDS(itemBlock.node->duration()), 3)));
|
||||
}
|
||||
else
|
||||
{
|
||||
lay->addRow("Event:", new QLabel(name));
|
||||
}
|
||||
|
||||
if (itemBlock.per_thread_stats)
|
||||
{
|
||||
lay->addRow("%/Thread:", new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration, item->root()->active_time))));
|
||||
lay->addRow("N/Thread:", new QLabel(QString::number(itemBlock.per_thread_stats->calls_number)));
|
||||
if (itemBlock.per_parent_stats->parent_block == item->threadId())
|
||||
|
||||
if (itemDesc.type() == ::profiler::BLOCK_TYPE_BLOCK)
|
||||
{
|
||||
auto percent = ::profiler_gui::percentReal(itemBlock.node->duration(), item->root()->active_time);
|
||||
lay->addRow("%:", new QLabel(percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent))));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto percent = ::profiler_gui::percentReal(itemBlock.node->duration(), blocksTree(itemBlock.per_parent_stats->parent_block).node->duration());
|
||||
lay->addRow("%:", new QLabel(percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent))));
|
||||
lay->addRow("%/Thread:", new QLabel(QString::number(::profiler_gui::percent(itemBlock.per_thread_stats->total_duration, item->root()->active_time))));
|
||||
if (itemBlock.per_parent_stats->parent_block == item->threadId())
|
||||
{
|
||||
auto percent = ::profiler_gui::percentReal(itemBlock.node->duration(), item->root()->active_time);
|
||||
lay->addRow("%:", new QLabel(percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent))));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto percent = ::profiler_gui::percentReal(itemBlock.node->duration(), blocksTree(itemBlock.per_parent_stats->parent_block).node->duration());
|
||||
lay->addRow("%:", new QLabel(percent < 0.5001 ? QString::number(percent, 'f', 2) : QString::number(static_cast<int>(0.5 + percent))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,6 +197,8 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
}
|
||||
|
||||
|
||||
const auto MIN_WIDTH = EASY_GLOBALS.enable_zero_length ? 0.f : 0.25f;
|
||||
|
||||
|
||||
// Iterate through layers and draw visible items
|
||||
if (gotItems)
|
||||
@ -299,8 +301,9 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto item_width = ::std::max(item.width(), MIN_WIDTH);
|
||||
auto x = item.left() * currentScale - dx;
|
||||
auto w = item.width() * currentScale;
|
||||
auto w = item_width * currentScale;
|
||||
if (x + w <= prevRight)
|
||||
{
|
||||
// This item is not visible
|
||||
@ -468,8 +471,9 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
if (item.left() < sceneRight && item.right() > sceneLeft)
|
||||
{
|
||||
const auto& itemBlock = easyBlock(item.block);
|
||||
const auto item_width = ::std::max(item.width(), MIN_WIDTH);
|
||||
auto top = levelY(guiblock.graphics_item_level);
|
||||
auto w = ::std::max(item.width() * currentScale, 1.0);
|
||||
auto w = ::std::max(item_width * currentScale, 1.0);
|
||||
decltype(top) h = (!itemBlock.expanded ||
|
||||
(w < EASY_GLOBALS.blocks_narrow_size && EASY_GLOBALS.hide_narrow_children))
|
||||
? (itemBlock.tree.depth * ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + ::profiler_gui::GRAPHICS_ROW_SIZE)
|
||||
@ -652,13 +656,15 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
if (left > sceneRight)
|
||||
break; // This is first totally invisible item. No need to check other items.
|
||||
|
||||
decltype(left) width = 0.25;
|
||||
decltype(left) width = MIN_WIDTH;
|
||||
if (left + width < sceneLeft) // This item is not visible
|
||||
continue;
|
||||
|
||||
left *= currentScale;
|
||||
left -= dx;
|
||||
width *= currentScale;
|
||||
if (width < 2) width = 2;
|
||||
|
||||
if (left + width <= prevRight) // This item is not visible
|
||||
continue;
|
||||
|
||||
@ -757,7 +763,7 @@ void EasyGraphicsItem::getBlocks(qreal _left, qreal _right, ::profiler_gui::Tree
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const ::profiler_gui::EasyBlockItem* EasyGraphicsItem::intersect(const QPointF& _pos) const
|
||||
const ::profiler_gui::EasyBlock* EasyGraphicsItem::intersect(const QPointF& _pos, ::profiler::block_index_t& _blockIndex) const
|
||||
{
|
||||
if (m_levels.empty() || m_levels.front().empty())
|
||||
{
|
||||
@ -772,7 +778,8 @@ const ::profiler_gui::EasyBlockItem* EasyGraphicsItem::intersect(const QPointF&
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto bottom = top + m_levels.size() * ::profiler_gui::GRAPHICS_ROW_SIZE_FULL;
|
||||
static const auto OVERLAP = ::profiler_gui::THREADS_ROW_SPACING >> 1;
|
||||
const auto bottom = top + m_levels.size() * ::profiler_gui::GRAPHICS_ROW_SIZE_FULL + OVERLAP;
|
||||
if (bottom < _pos.y())
|
||||
{
|
||||
return nullptr;
|
||||
@ -781,10 +788,58 @@ const ::profiler_gui::EasyBlockItem* EasyGraphicsItem::intersect(const QPointF&
|
||||
const unsigned int levelIndex = static_cast<unsigned int>(_pos.y() - top) / ::profiler_gui::GRAPHICS_ROW_SIZE_FULL;
|
||||
if (levelIndex >= m_levels.size())
|
||||
{
|
||||
// The Y position is out of blocks range
|
||||
|
||||
if (EASY_GLOBALS.enable_event_indicators && !m_pRoot->events.empty())
|
||||
{
|
||||
// If event indicators are enabled then try to intersect with one of event indicators
|
||||
|
||||
const auto& sceneView = view();
|
||||
auto first = ::std::lower_bound(m_pRoot->events.begin(), m_pRoot->events.end(), _pos.x(), [&sceneView](::profiler::block_index_t _index, qreal _value)
|
||||
{
|
||||
return sceneView->time2position(blocksTree(_index).node->begin()) < _value;
|
||||
});
|
||||
|
||||
if (first != m_pRoot->events.end())
|
||||
{
|
||||
if (first != m_pRoot->events.begin())
|
||||
--first;
|
||||
}
|
||||
else if (!m_pRoot->events.empty())
|
||||
{
|
||||
first = m_pRoot->events.begin() + m_pRoot->events.size() - 1;
|
||||
}
|
||||
|
||||
const auto MIN_WIDTH = EASY_GLOBALS.enable_zero_length ? 0.f : 0.25f;
|
||||
const auto currentScale = sceneView->scale();
|
||||
const auto dw = 5. / currentScale;
|
||||
|
||||
for (auto it = first, end = m_pRoot->events.end(); it != end; ++it)
|
||||
{
|
||||
_blockIndex = *it;
|
||||
const auto& item = easyBlock(_blockIndex);
|
||||
auto left = sceneView->time2position(item.tree.node->begin());
|
||||
|
||||
if (left - dw > _pos.x())
|
||||
break; // This is first totally invisible item. No need to check other items.
|
||||
|
||||
decltype(left) width = MIN_WIDTH;
|
||||
if (left + width + dw < _pos.x()) // This item is not visible
|
||||
continue;
|
||||
|
||||
return &item;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The Y position is inside blocks range
|
||||
|
||||
const auto MIN_WIDTH = EASY_GLOBALS.enable_zero_length ? 0.f : 0.25f;
|
||||
|
||||
const auto currentScale = view()->scale();
|
||||
const auto dw = 5. / currentScale;
|
||||
unsigned int i = 0;
|
||||
size_t itemIndex = ::std::numeric_limits<size_t>::max();
|
||||
size_t firstItem = 0, lastItem = static_cast<unsigned int>(level0.size());
|
||||
@ -814,20 +869,23 @@ const ::profiler_gui::EasyBlockItem* EasyGraphicsItem::intersect(const QPointF&
|
||||
const auto& item = level[itemIndex];
|
||||
static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max(item.children_begin);
|
||||
|
||||
if (item.left() > _pos.x())
|
||||
if (item.left() - dw > _pos.x())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (item.right() < _pos.x())
|
||||
const auto item_width = ::std::max(item.width(), MIN_WIDTH);
|
||||
if (item.left() + item_width + dw < _pos.x())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto w = item.width() * currentScale;
|
||||
if (i == levelIndex || (w < EASY_GLOBALS.blocks_narrow_size && EASY_GLOBALS.hide_narrow_children) || !easyBlock(item.block).expanded)
|
||||
const auto w = item_width * currentScale;
|
||||
const auto& guiItem = easyBlock(item.block);
|
||||
if (i == levelIndex || (w < EASY_GLOBALS.blocks_narrow_size && EASY_GLOBALS.hide_narrow_children) || !guiItem.expanded)
|
||||
{
|
||||
return &item;
|
||||
_blockIndex = item.block;
|
||||
return &guiItem;
|
||||
}
|
||||
|
||||
if (item.children_begin == MAX_CHILD_INDEX)
|
||||
@ -909,15 +967,16 @@ const ::profiler_gui::EasyBlock* EasyGraphicsItem::intersectEvent(const QPointF&
|
||||
else if (firstSync != m_pRoot->sync.begin())
|
||||
--firstSync;
|
||||
|
||||
const auto dw = 4. / view()->scale();
|
||||
for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it)
|
||||
{
|
||||
const auto& item = easyBlock(*it);
|
||||
|
||||
const auto left = sceneView->time2position(item.tree.node->begin()) - 2;
|
||||
const auto left = sceneView->time2position(item.tree.node->begin()) - dw;
|
||||
if (left > _pos.x())
|
||||
break;
|
||||
|
||||
const auto right = sceneView->time2position(item.tree.node->end()) + 2;
|
||||
const auto right = sceneView->time2position(item.tree.node->end()) + dw;
|
||||
if (right < _pos.x())
|
||||
continue;
|
||||
|
||||
|
@ -148,7 +148,7 @@ public:
|
||||
\param _blocks Reference to the array of selected blocks */
|
||||
void getBlocks(qreal _left, qreal _right, ::profiler_gui::TreeBlocks& _blocks) const;
|
||||
|
||||
const ::profiler_gui::EasyBlockItem* intersect(const QPointF& _pos) const;
|
||||
const ::profiler_gui::EasyBlock* intersect(const QPointF& _pos, ::profiler::block_index_t& _blockIndex) const;
|
||||
const ::profiler_gui::EasyBlock* intersectEvent(const QPointF& _pos) const;
|
||||
|
||||
private:
|
||||
|
@ -68,6 +68,7 @@ namespace profiler_gui {
|
||||
, connected(false)
|
||||
, enable_event_indicators(true)
|
||||
, enable_statistics(true)
|
||||
, enable_zero_length(true)
|
||||
, draw_graphics_items_borders(true)
|
||||
, hide_narrow_children(false)
|
||||
, display_only_relevant_stats(true)
|
||||
|
@ -118,6 +118,7 @@ namespace profiler_gui {
|
||||
bool connected; ///< Is connected to source (to be able to capture profiling information)
|
||||
bool enable_event_indicators; ///< Enable event indicators painting (These are narrow rectangles at the bottom of each thread)
|
||||
bool enable_statistics; ///< Enable gathering and using statistics (Disable if you want to consume less memory)
|
||||
bool enable_zero_length; ///< Enable zero length blocks (if true, then such blocks will have width == 1 pixel on each scale)
|
||||
bool draw_graphics_items_borders; ///< Draw borders for graphics blocks or not
|
||||
bool hide_narrow_children; ///< Hide children for narrow graphics blocks
|
||||
bool display_only_relevant_stats; ///< Display only relevant information in ProfTreeWidget (excludes min, max, average times if there are only 1 calls number)
|
||||
|
@ -261,6 +261,11 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("127.0.0.1"), m_lastP
|
||||
action->setChecked(EASY_GLOBALS.only_current_thread_hierarchy);
|
||||
connect(action, &QAction::triggered, this, &This::onHierarchyFlagChange);
|
||||
|
||||
action = submenu->addAction("Enable zero length blocks");
|
||||
action->setCheckable(true);
|
||||
action->setChecked(EASY_GLOBALS.enable_zero_length);
|
||||
connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.enable_zero_length = _checked; refreshDiagram(); });
|
||||
|
||||
action = submenu->addAction("Collapse items on tree reset");
|
||||
action->setCheckable(true);
|
||||
action->setChecked(EASY_GLOBALS.collapse_items_on_tree_close);
|
||||
@ -608,6 +613,15 @@ void EasyMainWindow::clear()
|
||||
m_bNetworkFileRegime = false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EasyMainWindow::refreshDiagram()
|
||||
{
|
||||
static_cast<EasyGraphicsViewWidget*>(m_graphicsView->widget())->view()->scene()->update();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EasyMainWindow::onDeleteClicked(bool)
|
||||
{
|
||||
auto button = QMessageBox::question(this, "Clear all profiled data", "All profiled data is going to be deleted!\nContinue?", QMessageBox::Yes, QMessageBox::No);
|
||||
@ -636,13 +650,13 @@ void EasyMainWindow::onChronoTextPosChanged(bool)
|
||||
{
|
||||
auto _sender = qobject_cast<QAction*>(sender());
|
||||
EASY_GLOBALS.chrono_text_position = static_cast<::profiler_gui::ChronometerTextPosition>(_sender->data().toInt());
|
||||
static_cast<EasyGraphicsViewWidget*>(m_graphicsView->widget())->view()->scene()->update();
|
||||
refreshDiagram();
|
||||
}
|
||||
|
||||
void EasyMainWindow::onEventIndicatorsChange(bool _checked)
|
||||
{
|
||||
EASY_GLOBALS.enable_event_indicators = _checked;
|
||||
static_cast<EasyGraphicsViewWidget*>(m_graphicsView->widget())->view()->scene()->update();
|
||||
refreshDiagram();
|
||||
}
|
||||
|
||||
void EasyMainWindow::onEnableDisableStatistics(bool _checked)
|
||||
@ -672,13 +686,13 @@ void EasyMainWindow::onEnableDisableStatistics(bool _checked)
|
||||
void EasyMainWindow::onDrawBordersChanged(bool _checked)
|
||||
{
|
||||
EASY_GLOBALS.draw_graphics_items_borders = _checked;
|
||||
static_cast<EasyGraphicsViewWidget*>(m_graphicsView->widget())->view()->scene()->update();
|
||||
refreshDiagram();
|
||||
}
|
||||
|
||||
void EasyMainWindow::onHideNarrowChildrenChanged(bool _checked)
|
||||
{
|
||||
EASY_GLOBALS.hide_narrow_children = _checked;
|
||||
static_cast<EasyGraphicsViewWidget*>(m_graphicsView->widget())->view()->scene()->update();
|
||||
refreshDiagram();
|
||||
}
|
||||
|
||||
void EasyMainWindow::onCollapseItemsAfterCloseChanged(bool _checked)
|
||||
@ -733,19 +747,19 @@ void EasyMainWindow::onCollapseAllClicked(bool)
|
||||
void EasyMainWindow::onSpacingChange(int _value)
|
||||
{
|
||||
EASY_GLOBALS.blocks_spacing = _value;
|
||||
static_cast<EasyGraphicsViewWidget*>(m_graphicsView->widget())->view()->scene()->update();
|
||||
refreshDiagram();
|
||||
}
|
||||
|
||||
void EasyMainWindow::onMinSizeChange(int _value)
|
||||
{
|
||||
EASY_GLOBALS.blocks_size_min = _value;
|
||||
static_cast<EasyGraphicsViewWidget*>(m_graphicsView->widget())->view()->scene()->update();
|
||||
refreshDiagram();
|
||||
}
|
||||
|
||||
void EasyMainWindow::onNarrowSizeChange(int _value)
|
||||
{
|
||||
EASY_GLOBALS.blocks_narrow_size = _value;
|
||||
static_cast<EasyGraphicsViewWidget*>(m_graphicsView->widget())->view()->scene()->update();
|
||||
refreshDiagram();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -858,6 +872,10 @@ void EasyMainWindow::loadSettings()
|
||||
if (!flag.isNull())
|
||||
EASY_GLOBALS.only_current_thread_hierarchy = flag.toBool();
|
||||
|
||||
flag = settings.value("enable_zero_length");
|
||||
if (!flag.isNull())
|
||||
EASY_GLOBALS.enable_zero_length = flag.toBool();
|
||||
|
||||
flag = settings.value("bind_scene_and_tree_expand_status");
|
||||
if (!flag.isNull())
|
||||
EASY_GLOBALS.bind_scene_and_tree_expand_status = flag.toBool();
|
||||
@ -914,6 +932,7 @@ void EasyMainWindow::saveSettingsAndGeometry()
|
||||
settings.setValue("collapse_items_on_tree_close", EASY_GLOBALS.collapse_items_on_tree_close);
|
||||
settings.setValue("all_items_expanded_by_default", EASY_GLOBALS.all_items_expanded_by_default);
|
||||
settings.setValue("only_current_thread_hierarchy", EASY_GLOBALS.only_current_thread_hierarchy);
|
||||
settings.setValue("enable_zero_length", EASY_GLOBALS.enable_zero_length);
|
||||
settings.setValue("bind_scene_and_tree_expand_status", EASY_GLOBALS.bind_scene_and_tree_expand_status);
|
||||
settings.setValue("enable_event_indicators", EASY_GLOBALS.enable_event_indicators);
|
||||
settings.setValue("enable_statistics", EASY_GLOBALS.enable_statistics);
|
||||
|
@ -264,6 +264,8 @@ private:
|
||||
|
||||
void clear();
|
||||
|
||||
void refreshDiagram();
|
||||
|
||||
void loadFile(const QString& filename);
|
||||
void readStream(::std::stringstream& data);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user