I build up a ruse of CustomWidgets in a QListWidget. So far, it works well.
A problem occurs when I move the vertical scroll bar downwards and want to display items that were not previously visible.
Then they are inserted as transparent rectangles, I can click them and respond to the clicks, but they are not drawn. They stay transparent!
I insert a picture to get a rough idea of the problem:
In my QListWidget I try to repaint it, but it doesn't work:
QListWidget::verticalScrollbarValueChanged(value);
auto item = this->itemAt(QPoint(24, value));
if (!item)
{
return;
}
auto widget = this->itemWidget(item);
if (!widget)
{
return;
}
// widget->resize(widget->size());
// widget->repaint();
widget->update();
What can I do? Thanks for help!
EDIT:
According to the request of Martin, I insert here a routine, which shows the addition of items:
void ListControl::AddCustomWidget(QWidget* customWidget, const QSize& size, bool forceSize)
{
if (forceSize)
{
customWidget->adjustSize();
}
auto displaySize = customWidget->size();
auto width = size.width();
auto height = size.height();
auto item = new QListWidgetItem(this);
this->addItem(item);
if (width >= 0)
{
displaySize.setWidth(width);
}
else
{
displaySize.setWidth(displaySize.width() - (this->verticalScrollBar()->width() + this->rightSpace));
}
if (height >= 0)
{
displaySize.setHeight(height);
}
item->setSizeHint(displaySize);
this->setItemWidget(item, customWidget);
}
Related
So I'm trying to scale an Image to fully fit a label from top to bottom. The problem is the outputs from the labels width, and height is not pixel values so I'm not sure how to correctly go about this. Here's my code.
void MainWindow::drawPixmap()
{
int labelWidth = ui->picLabel->width();
int labelHeight = ui->picLabel->height();
if(labelWidth <= labelHeight){
pixMap = this->pixMap.scaledToWidth(labelWidth);
ui->picLabel->setPixmap(pixMap);
}
else{
pixMap = this->pixMap.scaledToHeight(labelHeight);
ui->picLabel->setPixmap(pixMap);
}
}
Here's a visual of my problem, the black box is a border around the label box. I'm trying to get the image in the center to have its top and bottom touch the black box on respective sides.
Thanks for any help!
The problem could be when you calling this function. If you have called this function prior to making the label visible, then the size of the label could be incorrect after the label becomes visible.
Also, think about what happens when the label resize (if ever). You will still have to update the size of pixmap.
If your label is never going to change size, try calling it after the label has become visible.
Alternatively, try setting QLabel::setScaledContents(true)
If both the above doesn't work, try installing an eventFilter on the label to get the label's size change event, in there, you could do your above code
MainWindow::MainWindow()
{
....
ui->picLabel->installEventFilter(this);
}
bool MainWindow::eventFilter(QObject* object, QEvent* event)
{
bool res = Base::eventFilter(object, event);
if (object == ui->picLabel && event->type() == QEvent::Resize)
{
int labelWidth = ui->picLabel->width();
int labelHeight = ui->picLabel->height();
if(labelWidth <= labelHeight){
pixMap = this->pixMap.scaledToWidth(labelWidth);
ui->picLabel->setPixmap(pixMap);
}
else{
pixMap = this->pixMap.scaledToHeight(labelHeight);
ui->picLabel->setPixmap(pixMap);
}
}
return res;
}
Note: This a self answered question. It cause me some headache to solve it in past, so I think it is worth to share.
I have a qml application designed for HD resolution(1366x768). It's using QtQuick.Layouts, so adaptable to custom resolution. But resizing it to less than HD resolution makes it squishy and meaningless. I am not restricting QQuickWidget's size with minimum size, cause now I'm trying to place multiple of them in grid layout of a QWidget. I want to scale down root item to fit in widget when size of QQuickWidget is less than initial size(1366x768). Problem is QQuickWidget offers only two ResizeMode options and none of them suits to my needs. And it is not possible deactiave ResizeMode. So I'm trying to disable ResizeMode and write a custom one.
It's kinda ugly but working solution. I checked the source code of QQuickWidget, and realized inner updateSize function does nothing when ResizeMode is invalid.
CustomQuickWidget(QWidget* parent = nullptr)
: QQuickWidget(parent)
{
//set invalid resize mode for custom resizing
setResizeMode(static_cast<QQuickWidget::ResizeMode>(-1));
setSource(QML_SOURCE);
}
void CustomQuickWidget::resizeEvent(QResizeEvent *event) {
QQuickWidget::resizeEvent(event);
const int eventWidth = event->size().width();
const int eventHeight = event->size().height();
const int initialWidth = initialSize().width();
const int initialHeight = initialSize().height();
if (eventWidth >= initialWidth && eventHeight >= initialHeight) {
// SizeRootObjectToView
rootObject()->setSize(event->size());
rootObject()->setScale(1);
}
else {
// Scale down
const qreal widthScale = qreal(eventWidth) / initialWidth;
const qreal heightScale = qreal(eventHeight) / initialHeight;
if (widthScale < heightScale) {
// stretch height to fill
rootObject()->setWidth(initialWidth);
rootObject()->setHeight(qMin(int(eventHeight / widthScale), maximumHeight()));
rootObject()->setScale(widthScale);
}
else {
// stretch width to fill
rootObject()->setWidth(qMin(int(eventWidth / heightScale), maximumWidth()));
rootObject()->setHeight(initialHeight);
rootObject()->setScale(heightScale);
}
}
}
QSize CustomQuickWidget::sizeHint() const { return initialSize(); }
Make sure transformOrigin of root item is TopLeft.
transformOrigin: Item.TopLeft
Hey I can't find an answer to this anywhere. I want to change my menuItemLabel's font colour when touched. By default it scales and animates to be bigger when you first touch it, but how to customize it more? Here is my label and menuitem:
//add options label
optionsLabelSettings = Label::createWithTTF("OPTIONS", "fonts/font1.ttf", 140);
optionsLabelSettings->setColor(Color3B(255,255,255));
auto optionsItem = MenuItemLabel::create(optionsLabelSettings, CC_CALLBACK_1(MainMenuScene::GoToOptionsScene, this));
optionsItem->setPosition( Point (visibleSize.width/2 + origin.x, visibleSize.height /2));
//add menu
auto menu = Menu::create(optionsItem, NULL);
menu->setPosition( Point(0,0));
this->addChild(menu);
If you want to change font color, you can insert such method in your handler:
void MainMenuScene::GoToOptionsScene(parameters)
{
optionsItem->getLabel()->setColor(Color3B::BLACK); // or any other color
… // your method of switching to another scene
}
If you switch to another scene after pressing, it will be ok.
But if you plan to stay on the current scene, this method is not suitable, because you can’t to restore the font color back. In this case better way is using MenuItemImage and creating images for normal and selected states:
MenuItemImage *optionsItem = MenuItemImage::create(«normalImage.png», «selectedImage.png», CC_CALLBACK_1(MainMenuScene::GoToOptionsScene, this));
Or using ui::Button, if you haven’t images:
ui::Button* optionsItem = ui::Button::create();
optionsItem->setPosition(…);
optionsItem->setTitleText(«OPTIONS»);
optionsItem->setTitleFontName("fonts/font1.ttf");
optionsItem->setTitleFontSize(140);
optionsItem->setTitleColor(Color3B::WHITE);
optionsItem->addTouchEventListener([&](Ref* sender, Widget::TouchEventType type){
switch (type)
{
case ui::Widget::TouchEventType::BEGAN:
this->startPressingGoToOptionsScene();
break;
case ui::Widget::TouchEventType::ENDED:
this->finishPressingGoToOptionsScene();
break;
default:
break;
}
});
this->addChild(optionsItem);
and then in each handler set different button behavior:
void MainMenuScene::startPressingGoToOptionsScene(parameters)
{
optionsItem->setTitleColor(Color3B::BLACK);
optionsItem->setTitleText(«…») // change anything else
…
}
void MainMenuScene::finishPressingGoToOptionsScene(parameters)
{
optionsItem->setTitleColor(Color3B::WHITE); // return original font color and other changes
…
}
customise your MenuItemLabel class & Implement following two methods :-
class ChangedMenuItemLabel : MenuItemLabel
{
bool ChangedMenuItemLabel::initWithLabel(cocos2d::Node *label, const ccMenuCallback &callback)
{
if (!MenuItemLabel::initWithLabel(label,callback)) {
return false;
}
return true;
}
void selected(){
this->setColor("your color");
}
void unselected(){
this->setColor("your color");//or nothing
}
}
In my view I have in the right part 3 UISlider and in the left part the user can slide the menu with a UIPanGestureRecognizer. Sometimes, when the user uses the slider, it also drag the view. I don't want this to happen. I have this code in my panLayer method:
- (IBAction)panLayer:(UIPanGestureRecognizer *)pan {
if (pan.state == UIGestureRecognizerStateChanged) {
CGPoint point= [pan translationInView:self.topLayer];
CGRect frame = self.topLayer.frame;
frame.origin.x = MIN(258.0f, self.layerPosition + point.x);
if (frame.origin.x < 0) frame.origin.x = 0;
self.topLayer.frame = frame;
}
if (pan.state == UIGestureRecognizerStateEnded) {
if (self.topLayer.frame.origin.x <=160) {
[self animateLayerToPoint:0];
}
else {
[self animateLayerToPoint:VIEW_HIDDEN];
}
}
}
I want to create an if statement with the x position. Something like this:
if (firstTouch.x < 150 && firstTouch.x > 0) {
//Add my previous code
}
How can I access to the firstTouch in the view, and if it is in this area, run the UIPanGestureRecognizer?
Thanks
This can be easily achieved by adopting UIGestureRecognizerDelegate and implementing the following method:
-(BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldReceiveTouch:(UITouch*)touch
{
return touch.view != myUISlider;
}
I have an Qgraphicsscene implemented as a class, then i use QGraphicsScene::mousePressEvent to add a QGraphicsRectItem, this item have also an implementation for QGraphicsRectItem::mousePressEvent, the problem is that the event in the rect item is propagated to the scene and when i click it is added a new rect item, but i want is that the events inside this item do not propagate to the scene, i try event->accept but the event is propagated, how i cant do that? thanks for any help.
here is my qgraphicsscene code:
#include "imageview.h"
ImageView::ImageView(QWidget *parent){
scene = new ImageScene(this);
setScene(scene);
//this->setMouseTracking(true);
this->setInteractive(true);
}
ImageScene::ImageScene(QWidget *parent){
current = NULL;
selection = new QRubberBand(QRubberBand::Rectangle,parent);
selection->setGeometry(QRect(10,10,20,20));
setSceneRect(0,0,500,500);
}
void ImageScene::mousePressEvent(QGraphicsSceneMouseEvent *event){
QGraphicsScene::mousePressEvent(event);
/*IGNORING THIS EVENT FROM QGRAPHICSRECTITEM*/
cout<<"image view"<<endl;
if(this->selectedItems().length() == 0){ /*WORKS BUT IN SOME IMPLEMENTATION IS A PROBLEM (WHEN I DELETE THE ITEM WITH A DELETE BUTTON THE EVENT IS FIRED AND ADD A NEW ITEM .)*/
origin = event->scenePos();
selection->show();
}
}
void ImageScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event){
if(selection->isVisible() && selection->rect().width() >= 20 && selection->rect().height() >= 20){
QGraphicsScene::mouseReleaseEvent(event);
ResizableRect * rselection = new ResizableRect();
//selection->origin = event->scenePos();
//selection->grabMouse();
cout<<"add"<<endl;
this->addItem(rselection);
rselection->setPos(selection->pos());
rselection->setRect(0,0,selection->rect().width(),selection->rect().height());
}
selection->hide();
}
void ImageScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
QGraphicsScene::mouseMoveEvent(event);
if(selection->isVisible()){
QPoint rorigin(origin.x(),origin.y());
int xdes = event->scenePos().x();
int ydes = event->scenePos().y();
xdes = xdes > 0? xdes:0;
ydes = ydes > 0? ydes:0;
xdes = xdes < this->width()?xdes:this->width();
ydes = ydes < this->height()?ydes:this->height();
QPoint rdest(xdes,ydes);
selection->setGeometry(QRect(rorigin,rdest).normalized());
}
}
Opposite to QWidgets, QGraphicsScene catches events before child items. It is described on Qt documentation.
For correctly work with that, use reimplementation of QGraphicsView instead of QGraphcisScene.
Reimplement mousePressEvent there.
At that moment you can determine item under mouse pointer.
it it is there - you can just call QGraphicsView::mousePressEvent();
It it is not - use your implementation for add new item.
It also allows you to separate behavior of different views.