#include "gtl_gui_log_viewer.h" #include #include #include #include #include #include namespace gtl { namespace gui { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * log_viewer */ log_viewer::log_viewer(QWidget *parent) : QWidget{parent} { QVBoxLayout* mainLayout = new QVBoxLayout; setLayout( mainLayout ); m_view = new MyTableVeiw(this); m_view->verticalHeader()->setVisible(false); m_view->setAutoScroll(false); m_view->verticalHeader()->setDefaultSectionSize(5); m_view->setSelectionMode(QAbstractItemView::ContiguousSelection); // * * * * * * * * * * * * * * * // * change color of selected cell const QColor hlClr = Qt::white; // highlight color to set const QColor txtClr = Qt::black; // highlighted text color to set QPalette p = palette(); p.setColor(QPalette::Highlight, hlClr); p.setColor(QPalette::HighlightedText, txtClr); setPalette(p); connect(m_view, &QTableView::clicked, this, &log_viewer::slt_table_click); connect(m_view->verticalScrollBar(), &QScrollBar::rangeChanged, this, &log_viewer::slt_set_scroll_range); connect(m_view->verticalScrollBar(), &QScrollBar::valueChanged, this, &log_viewer::slt_set_scroll_value); m_model = new log_viewer_model(this); connect(gtl::logger::getInstance(), >l::logger::sgn_send_to_viewer, m_model, &log_viewer_model::slt_append_string); m_proxy_model = new filter_proxy_model(this); m_proxy_model->setSourceModel(m_model); m_view->setModel(m_proxy_model); m_view->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Stretch); connect(m_model, &QAbstractTableModel::rowsAboutToBeInserted, this, &log_viewer::slt_rows_about_to_be_inserted); connect(m_model, &QAbstractTableModel::rowsInserted, this, &log_viewer::slt_data_changed); // * * * * * * * * * * * * * * * // * // * levels // * // * * * * * * * * * * * * * * * m_cbb_level = new QComboBox(); connect(m_cbb_level, &QComboBox::currentIndexChanged, this, &log_viewer::slt_check_levels); m_cbb_level->addItem("info"); m_cbb_level->addItem("warning"); m_cbb_level->addItem("error"); m_cbb_level->addItem("debug"); // * * * * * * * * * * * * * * * // * // * searc text // * // * * * * * * * * * * * * * * * m_le_search_string = new QLineEdit(); m_le_search_string->setClearButtonEnabled(true); m_le_search_string->setPlaceholderText("Search text ..."); connect(m_le_search_string, &QLineEdit::textChanged, this, &log_viewer::slt_update_search_string); // * * * * * * * * * * * * * * * // * // * date time filter // * // * * * * * * * * * * * * * * * QHBoxLayout* hl_date_time = new QHBoxLayout; m_le_dt_from = new QLineEdit(); m_le_dt_from->setClearButtonEnabled(true); m_action_le_dt_from = m_le_dt_from->addAction(this->style()->standardIcon(QStyle::SP_TitleBarNormalButton), QLineEdit::LeadingPosition); m_le_dt_from->setPlaceholderText("from ..."); connect(m_action_le_dt_from, &QAction::triggered, this, &log_viewer::slt_dialog_set_datetime); connect(m_le_dt_from, &QLineEdit::textChanged, this, &log_viewer::slt_update_dt); m_le_dt_to = new QLineEdit(); m_le_dt_to->setClearButtonEnabled(true); m_action_le_dt_to = m_le_dt_to->addAction(this->style()->standardIcon(QStyle::SP_TitleBarNormalButton), QLineEdit::LeadingPosition); m_le_dt_to->setPlaceholderText("to ..."); connect(m_action_le_dt_to, &QAction::triggered, this, &log_viewer::slt_dialog_set_datetime); connect(m_le_dt_to, &QLineEdit::textChanged, this, &log_viewer::slt_update_dt); QLabel* l_date_time = new QLabel(" - "); hl_date_time->addWidget(m_le_dt_from); hl_date_time->addWidget(l_date_time); hl_date_time->addWidget(m_le_dt_to); // * * * * * * * * * * * * * * * // * // * btn reset search // * // * * * * * * * * * * * * * * * m_b_reset_search = new QPushButton(); connect(m_b_reset_search, &QPushButton::clicked, this, &log_viewer::slt_reset_search); m_b_reset_search->setText("Reset search"); // * * * * * * * * * * * * * * * // * // * btn clear data // * // * * * * * * * * * * * * * * * m_b_clear_data = new QPushButton(); connect(m_b_clear_data, &QPushButton::clicked, [this]() { m_model->clear(); }); m_b_clear_data->setText("Clear"); QHBoxLayout* hl_common = new QHBoxLayout; hl_common->addWidget(m_cbb_level); hl_common->addWidget(m_le_search_string); hl_common->addLayout(hl_date_time); hl_common->addWidget(m_b_reset_search); hl_common->addWidget(m_b_clear_data); mainLayout->addLayout(hl_common); mainLayout->addWidget(m_view); } void log_viewer::slt_check_levels(int index) { QString str_mass_filter = ""; switch(index) { case 0: str_mass_filter += "INFO|WARNING|ERROR"; break; case 1: str_mass_filter += "WARNING|ERROR"; break; case 2: str_mass_filter += "ERROR"; break; case 3: str_mass_filter += "DEBUG"; break; } m_proxy_model->set_filter_level(str_mass_filter); } void log_viewer::slt_update_search_string(void) { if(m_le_search_string->text().isEmpty()) m_proxy_model->set_disable_search_string(); else { m_proxy_model->set_search_string(m_le_search_string->text()); m_proxy_model->set_enable_search_string(); } } void log_viewer::slt_reset_search(void) { m_le_search_string->setText(""); m_le_dt_from->setText(""); m_le_dt_to->setText(""); m_proxy_model->set_search_string(""); m_proxy_model->set_disable_search_string(); m_proxy_model->set_disable_date_filter(); } void log_viewer::slt_dialog_set_datetime(void) { QAction *action_sender = (QAction *)sender(); QVBoxLayout* layout = new QVBoxLayout; QTime timeNow = QTime::currentTime(); QSpacerItem *space_left = new QSpacerItem(10, 10, QSizePolicy::Expanding); QSpacerItem *space_right = new QSpacerItem(10, 10, QSizePolicy::Expanding); QLabel *l_hour = new QLabel(":"); QLabel *l_minute = new QLabel(":"); QSpinBox *hour = new QSpinBox(); hour->setMinimum(0); hour->setMaximum(23); hour->setValue(timeNow.hour()); QSpinBox *minute = new QSpinBox(); minute->setMinimum(0); minute->setMaximum(59); minute->setValue(timeNow.minute()); QSpinBox *second = new QSpinBox(); second->setMinimum(0); second->setMaximum(59); second->setValue(timeNow.second()); QHBoxLayout* layout_time = new QHBoxLayout; layout_time->addSpacerItem(space_left); layout_time->addWidget(hour); layout_time->addWidget(l_hour); layout_time->addWidget(minute); layout_time->addWidget(l_minute); layout_time->addWidget(second); layout_time->addSpacerItem(space_right); QPushButton *btn_ok = new QPushButton("&ОК"); btn_ok->setObjectName("btn_ok"); QPushButton *btn_cancel = new QPushButton("&Cancel"); btn_cancel->setObjectName("btn_cancel"); QHBoxLayout *layout_btn = new QHBoxLayout; layout_btn->addWidget(btn_ok); layout_btn->addWidget(btn_cancel); m_calendar_widget = new QCalendarWidget(this); layout->addWidget(m_calendar_widget); layout->addLayout(layout_time); layout->addLayout(layout_btn); m_dialog_set_datetime = new QDialog(this); m_dialog_set_datetime->setLayout(layout); m_dialog_set_datetime->setWindowTitle("Select date and time"); connect(btn_ok, &QPushButton::clicked, m_dialog_set_datetime, &QDialog::accept); connect(btn_cancel, &QPushButton::clicked, m_dialog_set_datetime, &QDialog::reject); QString result_str; if(m_dialog_set_datetime->exec() == QDialog::Accepted) { result_str = m_calendar_widget->selectedDate().toString("dd.MM.yyyy ") + QString("%1:").arg(hour->value(), 2, 10, QLatin1Char('0')) + QString("%1:").arg(minute->value(), 2, 10, QLatin1Char('0')) + QString("%1").arg(second->value(), 2, 10, QLatin1Char('0')); if(action_sender == m_action_le_dt_from) m_le_dt_from->setText(result_str); else if(action_sender == m_action_le_dt_to) m_le_dt_to->setText(result_str); } } void log_viewer::slt_update_dt(void) { if( (false == m_le_dt_from->text().isEmpty()) && (false == m_le_dt_to->text().isEmpty()) ) { QDateTime dt_from = QDateTime::fromString(m_le_dt_from->text(), "dd.MM.yyyy hh:mm:ss"); QDateTime dt_to = QDateTime::fromString(m_le_dt_to->text(), "dd.MM.yyyy hh:mm:ss"); if(dt_from > dt_to) { QMessageBox::warning(this, "Warning", "Date From must be earlier than Date To", QMessageBox::Ok, QMessageBox::Ok ); m_proxy_model->set_disable_date_filter(); } else { m_proxy_model->set_datetime_filter(dt_from, dt_to); m_proxy_model->set_enable_date_filter(); } } else m_proxy_model->set_disable_date_filter(); } void log_viewer::slt_rows_about_to_be_inserted() { m_prev_index = m_view->currentIndex(); m_prev_value = m_view->verticalScrollBar()->value(); } void log_viewer::slt_data_changed(void) { // this condition is necessary for the correct display of the last rows of the table if((m_scroll_max - m_view->verticalScrollBar()->value()) > 4 ) m_view->scrollTo( m_prev_index, QAbstractItemView::PositionAtCenter ); else m_view->scrollTo( m_prev_index, QAbstractItemView::PositionAtTop ); m_prev_value = m_view->verticalScrollBar()->value(); } void log_viewer::slt_set_scroll_range(int min, int max) { m_scroll_min = min; m_scroll_max = max; } void log_viewer::slt_table_click(const QModelIndex &index) { m_prev_index = index; m_view->setCurrentIndex(index); m_prev_value = m_view->verticalScrollBar()->value(); } void log_viewer::slt_set_scroll_value() { int curr_value = m_view->verticalScrollBar()->value(); // if scroll in top position - then start autoscroll if(curr_value == m_scroll_min) { m_prev_index = QModelIndex(); m_view->setCurrentIndex(m_prev_index); m_prev_value = curr_value; return; } // if not select cell if(m_prev_index == QModelIndex()) { m_prev_index = m_view->indexAt(m_view->rect().center()); m_view->setCurrentIndex(m_prev_index); m_prev_value = curr_value; } // if move slider or page UP/DOWN if(m_view->verticalScrollBar()->isSliderDown() || m_view->verticalScrollBar()->hasFocus() || m_view->verticalScrollBar()->underMouse()) { if(m_prev_value < curr_value) { int row = m_prev_index.row() + (curr_value - m_prev_value); if(row > m_scroll_max) row = m_scroll_max; m_prev_index = QModelIndex(m_view->model()->index(row, m_prev_index.column())); m_view->setCurrentIndex(m_prev_index); m_prev_value = curr_value; } else if(m_prev_value > curr_value) { int row = m_prev_index.row() - (m_prev_value - curr_value); if(row < m_scroll_min) row = m_scroll_min; m_prev_index = QModelIndex(m_view->model()->index(row, m_prev_index.column())); m_view->setCurrentIndex(m_prev_index); m_prev_value = curr_value; } else { m_prev_value = curr_value; } } else // if mouse whell active { if(m_view->flag_whell_rotate == MyTableVeiw::Rotate::DOWN) { m_view->flag_whell_rotate = MyTableVeiw::Rotate::NONE; if(m_prev_value < curr_value) { int row = m_prev_index.row() + (curr_value - m_prev_value); if(row > m_scroll_max) row = m_scroll_max; m_prev_index = QModelIndex(m_view->model()->index(row, m_prev_index.column())); m_view->setCurrentIndex(m_prev_index); m_prev_value = curr_value; } else { m_prev_value = curr_value; } } else if(m_view->flag_whell_rotate ==MyTableVeiw::Rotate::UP) { m_view->flag_whell_rotate = MyTableVeiw::Rotate::NONE; if(m_prev_value > curr_value) { int row = m_prev_index.row() - (m_prev_value - curr_value); if(row < m_scroll_min) row = m_scroll_min; m_prev_index = QModelIndex(m_view->model()->index(row, m_prev_index.column())); m_view->setCurrentIndex(m_prev_index); m_prev_value = curr_value; } else { m_prev_value = curr_value; } } } } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * MySortFilterProxyModel */ filter_proxy_model::filter_proxy_model(QObject *parent) : QSortFilterProxyModel(parent) { m_filter_level = "INFO|WARNING|ERROR|DEBUG"; m_search_string = ""; m_enable_text_search = false; m_enable_date_filter = false; } bool filter_proxy_model::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { // TEXT TAG: find rows containing search strings in Tag and Text if(m_enable_text_search) { if(false == m_search_string.isEmpty()) { QModelIndex indexTag = sourceModel()->index(sourceRow, 3, sourceParent); QModelIndex indexText = sourceModel()->index(sourceRow, 4, sourceParent); if( (false == (sourceModel()->data(indexTag).toString().contains(m_search_string, Qt::CaseInsensitive))) && (false == (sourceModel()->data(indexText).toString().contains(m_search_string, Qt::CaseInsensitive))) ) return false; } } // DATE TIME: find rows with correct date time if(m_enable_date_filter) { // get date QModelIndex index = sourceModel()->index(sourceRow, 1, sourceParent); QString check_dt = sourceModel()->data(index).toString() + " "; // get time index = sourceModel()->index(sourceRow, 2, sourceParent); check_dt += sourceModel()->data(index).toString(); if(false == check_date_in_range( QDateTime::fromString(check_dt, "dd.MM.yyyy hh:mm:ss.zzz")) ) return false; } // LEVEL: find rows with selected levels QModelIndex indexLevel = sourceModel()->index(sourceRow, 0, sourceParent); if(false == m_filter_level.isEmpty()) { QStringList list = m_filter_level.split("|"); if(!list.isEmpty()) { foreach(QString str, list) { if((sourceModel()->data(indexLevel).toString().contains(str) ) ) { return true; } } } } return false; } bool filter_proxy_model::check_date_in_range(QDateTime date) const { return ((date >= m_dt_from) && (date <= m_dt_to)); } void filter_proxy_model::set_filter_level(const QString &level) { m_filter_level = level; invalidateFilter(); } void filter_proxy_model::set_search_string(const QString &str) { m_search_string = str; } void filter_proxy_model::set_enable_search_string(void) { m_enable_text_search = true; invalidateFilter(); } void filter_proxy_model::set_disable_search_string(void) { m_enable_text_search = false; invalidateFilter(); } void filter_proxy_model::set_datetime_filter(QDateTime from, QDateTime to) { m_dt_from = from; m_dt_to = to; } void filter_proxy_model::set_enable_date_filter(void) { m_enable_date_filter = true; invalidateFilter(); } void filter_proxy_model::set_disable_date_filter(void) { m_enable_date_filter = false; invalidateFilter(); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * LogViewerModel */ log_viewer_model::log_viewer_model( QObject* parent ) : QAbstractTableModel( parent ) { } int log_viewer_model::rowCount( const QModelIndex& parent ) const { Q_UNUSED( parent ) return m_logs.count(); } int log_viewer_model::columnCount( const QModelIndex& parent ) const { Q_UNUSED( parent ) return LAST; } QVariant log_viewer_model::headerData( int section, Qt::Orientation orientation, int role ) const { if( role != Qt::DisplayRole ) { return QVariant(); } if( orientation == Qt::Vertical ) { return section; } switch( section ) { case LEVEL: return "Level"; case DATE: return "Date"; case TIME: return "Time"; case TAG: return "Tag"; case TEXT: return "Text"; } return QVariant(); } QVariant log_viewer_model::data( const QModelIndex& index, int role ) const { if( !index.isValid() || m_logs.count() <= index.row() || ( role != Qt::DisplayRole)) { return QVariant(); } return m_logs[ index.row() ][ Column( index.column() ) ]; } void log_viewer_model::slt_append_string( const QString& level, const QString& date, const QString& time, const QString& tag, const QString& text ) { LogViewerData log; log[ LEVEL ] = level; log[ DATE ] = date; log[ TIME ] = time; log[ TAG ] = tag; log[ TEXT ] = text; beginInsertRows( QModelIndex(), 0, 0 ); m_logs.insert(0, log ); endInsertRows(); emit sgnl_set_data(); } bool log_viewer_model::setData( const QModelIndex& index, const QVariant& value, int role ) { if( !index.isValid() || role != Qt::EditRole || m_logs.count() <= index.row() ) { return false; } m_logs[ index.row() ][ Column( index.column() ) ] = value; emit dataChanged( index, index ); return true; } void log_viewer_model::set_level_value(uint8_t val) { m_select_levels = val; } void log_viewer_model::clear() { beginResetModel(); m_logs.clear(); endResetModel(); } } }