I use physical Knob for control menu than included QCombobox. When press knob then call click function of ComboBoxObject. When knob rotate then rotate function call with value {-1,1}.
I need change highlighted item when QComboBox is popup in rotate function.
void ComboBoxObject::click(){
showPopup();
}
void ComboBoxObject::rotate(direct int) // Left=-1, Right=1
{
up/down
}
When focus on Combobox setCurrenIndex(index) work correct but if pupop list then it doesn't work. I use highlighted(index) but not work.
I try simulate key_up and key_dow for change highlighted item. highlighted item doesn't change.
void ComboBoxObject::rotate(direct int) // Left=-1, Right=1
{
QKeyEvent *event;
if(direct == 1){
event = new QKeyEvent ( QEvent::KeyPress, Qt::Key_Up,Qt::NoModifier);
}
else{
event = new QKeyEvent ( QEvent::KeyPress, Qt::Key_Down,Qt::NoModifier);
}
bool result = QApplication::sendEvent(this,event);
}
QComboBox doesn't have that functionality built in, but you can implement it yourself easily enough:
void rotateComboBox(QComboBox * theComboBox, int direction)
{
const int numItems = theComboBox->count();
const int curIndex = theComboBox->currentIndex();
int newIndex = curIndex+direction;
if (newIndex <= 0) newIndex = numItems-1; // wrap around
if (newIndex >= numItems) newIndex = 0; // wrap around
theComboBox->setCurrentIndex(newIndex);
}
Related
I have QVector m_vertices and QVector m_lines in my drawingWidget.h
I currently trying to do simple drawing tool, where i can manipulate with vertices and lines. I managed to draw multiple vertices on my "MainWindow", now i am trying to implement line between two vertices
The main idea is, that i have a pop-up menu, where i can choose a "tool". I can add vertex, remove vertex, move vertex, add line, delete line. The idea is , when i choose for example "Add Vertex" then the "m_state" will change to "Adding" so i can only add vertices. When i choose "Add Line" for example, then the "m_state" again will change, so that i can only choose two created vertices, that will result a line between them.
enum DrawingWidgetState {
NO_TOOL_SELECTED,
ADD_VERTEX_SELECTED,
MOVE_VERTEX_SELECTED,
DELETE_VERTEX_SELECTED,
ADD_LINE_SELECTED,
DELETE_LINE_SELECTED
};
Here, in the end, it turns out that if I click "Add Vertex" then when I put the second vertex, then it automatically puts a line between them
void DrawingWidget::paintEvent(QPaintEvent *event) {
QPainter painter(this);
painter.fillRect(event->rect(), Qt::blue);
painter.setBrush(Qt::black);
for(int i = 0; i < m_vertices.size() ; i++) {
painter.drawEllipse(m_vertices[i], 20, 20);
}
for(int i = 0; i < m_vertices.size()-1 ; i++)
{
const QPoint& point1 = m_vertices[i];
const QPoint& point2 = m_vertices[i+1];
painter.drawLine(point1, point2);
}
}
void DrawingWidget::mousePressEvent(QMouseEvent *event) {
if(m_state == ADD_VERTEX_SELECTED) {
if(event->button() == Qt::LeftButton) {
//m_x = event->x();
//m_y = event->y();
//update();
QPoint point = event->pos();
m_vertices.append(point);
update();
}
}
if(m_state == ADD_LINE_SELECTED) {
if(event->button() == Qt::LeftButton) {
for(int i = 0; i < m_vertices.size()-1 ; i++) {
const auto &point1 = m_vertices[i];
const auto &point2 = m_vertices[i+1];
}
}
}
}
Could someone explain to me why this happens and what should i do to improve my code ?
If something requires explanation, i will tell.
I have several cocos layouts they are various panels or menus. I was wondering how to close then with a touch input outside of their area like most apps.
Basically how to close a popup menu by tapping any empty area on screen.
Basically you calculate the spaces between the edges of the screen and your popup and then call the destructor.
This is how I do it:
In init
auto touchListener = cocos2d::EventListenerTouchOneByOne::create();
touchListener->setSwallowTouches(true);
touchListener->onTouchBegan = CC_CALLBACK_2(CLASS::onTouch, this);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, this);
On Touch
bool CLASS::onTouch(cocos2d::Touch* touch, cocos2d::Event* event)
{
int ySize = visibleSize.height - holder->getContentSize().height;
int locationY = touch->getLocation().y;
if((locationY > 0 && locationY < ySize/2) || (locationY > origin.y + ySize/2 + holder->getContentSize().height && locationY < visibleSize.height))
{
this->removeFromParentAndCleanup(true);
}
return true;
}
I have an object that inherits QTableView and overrides the resizeEvent() method to set the widths of the table columns to a percantage of the available space if the table as a whole is resized.
Code is as follows:
void DDUTableView::resizeEvent(QResizeEvent* ev)
{
int num_columns = NUM_ELEMENTS(COLUMN_WIDTHS);
if (num_columns > 0) {
int width = ev->size().width();
int used_width = 0;
// Set our widths to be a percentage of the available width
for (int i = 0; i < num_columns - 1; i++) {
int column_width = (width * COLUMN_WIDTHS[i]) / 100;
this->setColumnWidth(i, column_width);
used_width += column_width;
}
// Set our last column to the remaining width
this->setColumnWidth(num_columns - 1, width - used_width);
}
// Call our base resizeEvent to handle the vertical resizing which
// we don't bother with here
QTableView::resizeEvent(ev);
}
This all works fine until the user manually resizes one of the columns and stretches it beyond the viewport (bringing up the horizontal scrollbar). This then triggers my resizeEvent() call which resets the column widths to the percentage defaults.
I can overcome this issue by connecting to the sectionResized() signal on the table header and setting a timer running. If the resizeEvent() is called whilst the timer is active then I don't recalculate the table widths.
Code below:
connect(horizontalHeader(), SIGNAL(sectionResized(int, int, int)), this, SLOT(slotSectionResized(int, int, int)));
void DDUTableView::slotSectionResized(int /*logicalIndex*/, int /*oldSize*/, int /*newSize*/)
{
timer_->start(500);
}
void DDUTableView::resizeEvent(QResizeEvent* ev)
{
if (timer_->isActive()) {
return;
}
// etc
This works but is messy. Anyway the simple question is can I prevent the resizeEvent() from being called if the user manually adjusts the column headers beyond the scope of the viewport? Alternatively, if not, is it possible to identify in the resizeEvent() whether this particular situation has occurred without having to set timers and the like?
Checking if the scrollbar is visible worked for my scenario.
void DDUTableView::resizeEvent(QResizeEvent* ev)
{
if (!horizontalScrollBar()->isVisible()) {
int num_columns = NUM_ELEMENTS(COLUMN_WIDTHS);
if (num_columns > 0) {
int width = ev->size().width();
int used_width = 0;
// Set our widths to be a percentage of the available width
for (int i = 0; i < num_columns - 1; i++) {
int column_width = (width * COLUMN_WIDTHS[i]) / 100;
this->setColumnWidth(i, column_width);
used_width += column_width;
}
// Set our last column to the remaining width
this->setColumnWidth(num_columns - 1, width - used_width);
}
}
// Call our base resizeEvent to handle the vertical resizing which
// we don't bother with here
QTableView::resizeEvent(ev);
}
I want the height of a QListView based on an QAbstractListModel to fit the contents, if the amount of the items is smaller a given number N. If there are more than N items, it should show only N items. I read a lot of curious tips in the web, but most of them look like hacks. I guess this has something to do with sizeHint() but in the model view approach there is no ItemWidget in which I could override the sizeHint(). What is the correct way to achieve this behaviour?
Further, how does this correlate to the size policy of the parent app? This is a second constraint: The contents should not try to use the space they have in the parent widget, but the parent widget should resize to fit the QListView.
This is not a duplicate to this question, since I can't use QCompleter.
sizeHint() has to be overridden in the QListView (respectively your subclass of it). The mentioned special behaviour can be implemented there. eg like this:
QSize ProposalListView::sizeHint() const
{
if (model()->rowCount() == 0) return QSize(width(), 0);
int nToShow = _nItemsToShow < model()->rowCount() ? _nItemsToShow : model()->rowCount();
return QSize(width(), nToShow*sizeHintForRow(0));
}
This requires the size hint of the item delegate to be reasonable. In my case:
inline QSize sizeHint ( const QStyleOptionViewItem&, const QModelIndex& ) const override { return QSize(200, 48); }
Now I just have to call updateGeometry() after changing the model.
For those who use ListView in QML with QtQuick Controls.
I made the content fit with property anchors.bottomMargin.
anchors.bottomMargin: 20
I have faced the same problem. The solution marked as answer doesn't work correctly when you have scroll bars or you set a frame border. So here is my solution, which is up to date, that works for all QAbstractItemViews.
Since Qt 5.2 version QAbstractItemView has method setSizeAdjustPolicy, inherited from QAbstractScrollArea. If you set QAbstractScrollArea::AdjustToContents than the scroll area will always adjust to the viewport (content).
But the view by default can not be smaller than it's minimum size hint's. So here is what should be done to make an item view fully shrink when the model doesn't have any items:
Set setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents)
Override minimumSizeHint:
QSize minimumSizeHint() const override {
return QSize(0, 0);
}
Override viewportSizeHint (when model has no items, QTreeView and QListView has a default size hint for the viewport, so need to return (0,0) in those cases):
QSize viewportSizeHint() const override {
if (QAbstractItemView::sizeAdjustPolicy() != QAbstractScrollArea::AdjustToContents)
return T::viewportSizeHint();
// if QTableView is used, comment the block below
if (model() == nullptr)
return QSize(0, 0);
if (model()->rowCount() == 0)
return QSize(0, 0);
// T is your view type (QTreeView, QTableView, QListView, etc.)
return T::viewportSizeHint();
}
NOTE:
Don't forget to set sizePolicy or add stretch to the layout.
Not hidden scroll bars increase viewport's minimum size.
Also QListView had problems (bug report) with AdjustToContents flag, that were fixed since Qt 6.2. So if you use a version below 6.2 override viewportSizeHint like this:
QSize viewportSizeHint() const override
{
if (QAbstractItemView::sizeAdjustPolicy() != QAbstractScrollArea::AdjustToContents)
return T::viewportSizeHint();
if (std::is_same<T, QTreeView>::value || std::is_same<T, QListView>::value)
{
if (model() == nullptr)
return QSize(0, 0);
if (model()->rowCount() == 0 || model()->columnCount() == 0)
return QSize(0, 0);
}
if (std::is_same<T, QListView>::value)
{
const int rowCount = model()->rowCount();
int height = 0;
for (int i = 0; i < rowCount; i++) {
height += T::sizeHintForRow(i);
}
return QSize(T::viewportSizeHint().width(), height);
}
return T::viewportSizeHint();
}
There is no good way to do this. I use the following code.
Header:
class List_view_auto_height : public QObject {
Q_OBJECT
public:
explicit List_view_auto_height(QListView * target_list);
void set_max_auto_height(int value);
void set_min_height(int value);
private:
QListView* list;
QTimer timer;
int _min_height;
int _max_height;
bool eventFilter(QObject* object, QEvent* event);
private slots:
void update_height();
};
Source:
List_view_auto_height::List_view_auto_height(QListView *target_list) :
QObject(target_list)
, list(target_list)
{
_min_height = 0;
_max_height = 250;
connect(list->model(), &QAbstractItemModel::rowsInserted,
this, &List_view_auto_height::update_height);
connect(list->model(), &QAbstractItemModel::rowsRemoved,
this, &List_view_auto_height::update_height);
connect(list->model(), &QAbstractItemModel::layoutChanged,
this, &List_view_auto_height::update_height);
list->installEventFilter(this);
update_height();
connect(&timer, &QTimer::timeout, this, &List_view_auto_height::update_height);
timer.start(500);
}
void List_view_auto_height::set_max_auto_height(int value) {
_max_height = value;
update_height();
}
void List_view_auto_height::set_min_height(int value) {
_min_height = value;
update_height();
}
bool List_view_auto_height::eventFilter(QObject *object, QEvent *event) {
if (event->type() == QEvent::Show) {
update_height();
}
return false;
}
void List_view_auto_height::update_height() {
if (!list->isVisible()) { return; }
int height = 0;
if (list->model()->rowCount() > 0) {
height = list->visualRect(list->model()->index(list->model()->rowCount() - 1, 0)).bottom() + 1;
height -= list->visualRect(list->model()->index(0, 0)).top();
}
if (list->horizontalScrollBar()->isVisible()) {
height += list->horizontalScrollBar()->height();
}
bool scrollbar_enabled = false;
if (_max_height != 0 && height > _max_height) {
height = _max_height;
scrollbar_enabled = true;
}
if (height < _min_height) {
height = _min_height;
}
list->setFixedHeight(height + 6);
}
Usage:
new List_widget_auto_height(list);
It's full of hacks and can work incorrectly in some cases. Feel free to improve it.
It sets height using setFixedHeight. This should provide correct behavior for parent widget's size hint.
I have a simple script which displays one button (which is a png image) and when the user clicks it the application quits.
But i want to add multiple buttons which is where im finding my current thinking will lead to a very long if:else situation and I am wondering if that is the only way.
This is how i have my current menu set up in a main.cpp file.
bool handle_mouse_leftClick(int x, int y, SDL_Surface *button) {
if( ( ( mouseX > x ) && ( mouseX < x + button->w ) ) && ( ( mouseY > y ) && ( mouseY < y + button->h ) ) ) {
return true;
} else {
return false;
}
}
This is my detection function.
Below is my main function which acts as my game loop, i've removed non relevant code to keep it easier to follow:
//menu button
SDL_Surface *button;
button = IMG_Load("button.png");
while(!quit){
//handle events
while( SDL_PollEvent( &event ) ){
switch(event.type){
case SDL_QUIT: quit = true; break;
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT) {
if(handle_mouse_leftClick(btnx,btny,button)){
quit = true;
}
}
break;
}
}
The issue is should my main.cpp have all this checking going on, is going to get very long very quickly when i add more buttons so I'm wondering if I have missed a trick to simplify my efforts?
When you get right down to the basic logic/computation, I would say the answer is "no", you haven't missed any tricks. I've never found a way around checking each target one at a time - at least in terms of computation. You could make your code cleaner a lot of ways. You could have a GuiElement class that exposes a "bool IsIn( int x, int y )" method. Then your big case statement would look more like:
bool handle_mouse_leftClick(int x, int y, SDL_Surface *button)
{
if (Button1.IsIn( mouseX, mouseY )
{
// do this
}
else if (Button2.IsIn( mouseX, mouseY ))
{
// do that
}
else
{
return false;
}
}
You could then further reduce amount code with a list or table:
int handle_mouse_leftClick(int x, int y, SDL_Surface *button)
{
for (unsigned int i = 0; i < buttonList.size(); i++
{
if (buttonList[i].IsIn( mouseX, mouseY ))
{
return i; // return index of button pressed
}
return -1; // nothing pressed
}
}
But it's still ultimately looking at each rectangle one at a time.
Couple caveats:
I don't think there's much real computational overhead in checking the hit boxes of each item - unless you're doing it at some crazy high frame rate.
Sure, you could optimize the checking with some kind of spatial index (like a b-tree or quad-tree organized by the locations of the buttons on the screen), but ... see #1. ;-)
If instead of 10 or 15 buttons/controls you have THOUSANDS then you will likely want to do #2 because #1 will no longer be true.
********* Update ***********
Here's a brief sample of a class that could be used for this. As far as .h vs main.cpp, the typical approach is to put the header in a "Button.h" and the implementation (code) in a "Button.cpp", but you could just put this at the top of main.cpp to get started - it has all the logic right in the class definition.
You'll notice I didn't really write any new code. The "IsIn()" test is your logic verbatim, I just changed the variable names to match the class. And since you already have a single button, I'm assuming you can reuse the code that renders that button the Render() method.
And lastly, if is not something you're familiar with, you don't have to create a list/vector at all. The code the renders the buttons could just call "okButton.Render()", followed by "cancelButton.Render()".
Sample "button" class:
class Button
{
private:
int m_x, m_y; // coordinates of upper left corner of control
int m_width, m_height; // size of control
public:
Button(int x, int y, int width, int height, const char* caption)
{
m_x = x;
m_y = y;
m_width = width;
m_height = height;
// also store caption in variable of same type you're using now for button text
}
bool IsIn( int mouseX, int mouseY )
{
if (((mouseX > m_x) && (mouseX < m_x + m_width))
&& ((mouseY > m_y) && (mouseY < m_y + m_height ) ) ) {
return true;
} else {
return false;
}
}
void Render()
{
// use the same code you use now to render the button in OpenGL/SDL
}
};
Then to create it/them (using the list approach):
Button okButton( 10, 10, 100, 50, "OK" );
buttonList.push_back( okButton );
Button cancelButton( 150, 10, 100, 50, "Cancel" );
buttonList.push_back( cancelButton );
And in your render loop:
void Update()
{
for (unsigned int i = 0; i < buttonList.size(); i++
{
buttonList[i].Render();
}
}