In the following scala code I change foreground, horizontalAlignment and background to some values. However in the GUI these properties are not shown.
The horizontalAlignment remains centered.
The backgrould remains gray.
However the foreground (font color) changes according to the values.
How can I obtain the desired effects?
Thanks for any help!
import scala.swing._
object GuiTest extends SimpleSwingApplication {
def top = new MainFrame {
title = "Label Test"
val tempList = List("Celsius", "Fahrenheit", "Kelvin")
contents = bagPnl(tempList)
val fields = contents(0).peer.getComponents
val valuefields
= for (f <- 0 until fields.length / 2)
yield tempList(f) -> fields.apply(2 * f + 1).asInstanceOf[javax.swing.JLabel]
val tfm = valuefields.toMap[String, javax.swing.JLabel]
tfm.apply("Celsius").setText("35°C")
tfm.apply("Kelvin").setText("0 K")
}
def bagPnl(list: List[String]) = new GridBagPanel {
val gbc = new Constraints
gbc.insets = new Insets(3, 3, 3, 3)
var l = 0
for (title <- list) {
gbc.gridy = l
gbc.gridx = 0
/* title label */
add(new Label {
horizontalAlignment = Alignment.Left
foreground = java.awt.Color.RED
background = java.awt.Color.CYAN
text = title
}, gbc)
gbc.gridx = 1
/* value label */
val t = new Label {
horizontalAlignment = Alignment.Right
foreground = java.awt.Color.BLUE
background = java.awt.Color.YELLOW
name = title
}
t.background = java.awt.Color.GREEN
add(t, gbc)
l = l + 1
}
}
}
sorry I am not allowed to post images yet :-(
GridBagLayout is one hell of a layout manager. You'll be probably better of with GroupLayout, but there is no related panel type in Scala-Swing yet. (Here for an example).
The problem with the label positioning is that its alignment only makes sense when it is given more space than its preferred size. By default, the grid bag layout doesn't give it more space, and the centering is a result of its own alignment (not that of the label). The easiest here is to specify that the components can use up the horizontal space if available:
gbc.fill = GridBagPanel.Fill.Horizontal
The second questions concerns the background color of the label. Here is a related question. In short, by default the label is transparent and its background color ignored. You can switch to opaque painting:
new Label {
...
opaque = true
}
Related
In this image:
I would like to access the actual tabs, rather than the content, so I can set a QPropertyAnimation on the actual tab when it is hovered on. I know how to get the hover event working, and I can get the tab index on the hover, I just can't access the actual tab when I hover on it. Is there a list of the tabs somewhere as an attribute of the QTabBar or the QTabWidget, or where can I find the tabs? Or do I have to subclass the addTab function to create the tabs individually?
Extra Info
Using PyQt5.14.1
Windows 10
Python 3.8.0
You cannot access "tabs", as they are not objects, but an abstract representation of the contents of the tab bar list.
The only way to customize their appearance is by subclassing QTabBar and overriding the paintEvent().
In order to add an over effect, you have to provide a unique animation for each tab, so you have to keep track of all tabs that are inserted or removed. The addTab, insertTab and removeTab methods are not valid options, since they are not used by QTabWidget. It uses instead tabInserted() and tabRemoved(), so those are to be overridden too.
This could be a problem with stylesheets, though, especially if you want to set fonts or margins.
Luckily, we can use the qproperty-* declaration with custom PyQt properties, and in the following example I'm using them for the tab colors.
class AnimatedTabBar(QtWidgets.QTabBar):
def __init__(self, *args):
super().__init__(*args)
palette = self.palette()
self._normalColor = palette.color(palette.Dark)
self._hoverColor = palette.color(palette.Mid)
self._selectedColor = palette.color(palette.Light)
self.animations = []
self.lastHoverTab = -1
#QtCore.pyqtProperty(QtGui.QColor)
def normalColor(self):
return self._normalColor
#normalColor.setter
def normalColor(self, color):
self._normalColor = color
for ani in self.animations:
ani.setEndValue(color)
#QtCore.pyqtProperty(QtGui.QColor)
def hoverColor(self):
return self._hoverColor
#hoverColor.setter
def hoverColor(self, color):
self._hoverColor = color
for ani in self.animations:
ani.setStartValue(color)
#QtCore.pyqtProperty(QtGui.QColor)
def selectedColor(self):
return self._selectedColor
#selectedColor.setter
def selectedColor(self, color):
self._selectedColor = color
self.update()
def tabInserted(self, index):
super().tabInserted(index)
ani = QtCore.QVariantAnimation()
ani.setStartValue(self.normalColor)
ani.setEndValue(self.hoverColor)
ani.setDuration(150)
ani.valueChanged.connect(self.update)
self.animations.insert(index, ani)
def tabRemoved(self, index):
super().tabRemoved(index)
ani = self.animations.pop(index)
ani.stop()
ani.deleteLater()
def event(self, event):
if event.type() == QtCore.QEvent.HoverMove:
tab = self.tabAt(event.pos())
if tab != self.lastHoverTab:
if self.lastHoverTab >= 0:
lastAni = self.animations[self.lastHoverTab]
lastAni.setDirection(lastAni.Backward)
lastAni.start()
if tab >= 0:
ani = self.animations[tab]
ani.setDirection(ani.Forward)
ani.start()
self.lastHoverTab = tab
elif event.type() == QtCore.QEvent.Leave:
if self.lastHoverTab >= 0:
lastAni = self.animations[self.lastHoverTab]
lastAni.setDirection(lastAni.Backward)
lastAni.start()
self.lastHoverTab = -1
return super().event(event)
def paintEvent(self, event):
selected = self.currentIndex()
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
style = self.style()
fullTabRect = QtCore.QRect()
tabList = []
for i in range(self.count()):
tab = QtWidgets.QStyleOptionTab()
self.initStyleOption(tab, i)
tabRect = self.tabRect(i)
fullTabRect |= tabRect
if i == selected:
# make the selected tab slightly bigger, but ensure that it's
# still within the tab bar rectangle if it's the first or the last
tabRect.adjust(
-2 if i else 0, 0,
2 if i < self.count() - 1 else 0, 1)
pen = QtCore.Qt.lightGray
brush = self._selectedColor
else:
tabRect.adjust(1, 1, -1, 1)
pen = QtCore.Qt.NoPen
brush = self.animations[i].currentValue()
tabList.append((tab, tabRect, pen, brush))
# move the selected tab to the end, so that it can be painted "over"
if selected >= 0:
tabList.append(tabList.pop(selected))
# ensure that we don't paint over the tab base
margin = max(2, style.pixelMetric(style.PM_TabBarBaseHeight))
qp.setClipRect(fullTabRect.adjusted(0, 0, 0, -margin))
for tab, tabRect, pen, brush in tabList:
qp.setPen(pen)
qp.setBrush(brush)
qp.drawRoundedRect(tabRect, 4, 4)
style.drawControl(style.CE_TabBarTabLabel, tab, qp, self)
class Example(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.tabWidget = QtWidgets.QTabWidget()
layout.addWidget(self.tabWidget)
self.tabBar = AnimatedTabBar(self.tabWidget)
self.tabWidget.setTabBar(self.tabBar)
self.tabWidget.addTab(QtWidgets.QCalendarWidget(), 'tab 1')
self.tabWidget.addTab(QtWidgets.QTableWidget(4, 8), 'tab 2')
self.tabWidget.addTab(QtWidgets.QGroupBox('Group'), 'tab 3')
self.tabWidget.addTab(QtWidgets.QGroupBox('Group'), 'tab 4')
self.setStyleSheet('''
QTabBar {
qproperty-hoverColor: rgb(128, 150, 140);
qproperty-normalColor: rgb(150, 198, 170);
qproperty-selectedColor: lightgreen;
}
''')
Some final notes:
I only implemented the top tab bar orientation, if you want to use tabs in the other directions, you'll have change the margins and rectangle adjustments;
remember that using stylesheets will break the appearence of the arrow buttons;(when tabs go beyond the width of the tab bar), you'll need to set them carefully
painting of movable (draggable) tabs is broken;
right now I don't really know how to fix that;
I wonder how to set the text "Highlight" of a part of text inside tlfTextField with the code?
I tried "tf.backgroundColor = 0x990000" property, but did not help.
For instance, I can change the Font Color of any contents inside Parenthesis, by this code:
private function decorate():void {
var tf:TextFormat = new TextFormat();
tf.color = 0x990000;
var startPoint:int = 0;
while (startPoint != -1) {
var n1:int = textMc.tlfText.text.indexOf("(", startPoint);
var n2:int = textMc.tlfText.text.indexOf(")", n1 + 1);
if (n1 == -1 || n2 == -1) {
return;
}
textMc.tlfText.setTextFormat(tf, n1 + 1, n2);
startPoint = n2 + 1;
}
}
So I know "tf.color = 0x990000;" will change the Font color, however, don't know how to "highlight" some text, with code, as I do inside Flash manually.
You should have probably used tlfMarkup property to set the required format to the specific part of text. The attributes you seek are backgroundColor and backgroundAlpha of the span XML element that you should wrap your selection, however it should be much more difficult should there already be spans around words when you retrieve the property from your text field.
The problem with your solution is that you don't check if the two characters are located on a single line before drawing your rectangle, also you would need to redraw such rectangles each time something happens with the textfield. The proposed approach makes use of Flash HTML renderer's capabilities to preserve the formatting, however it will require a lot of work to handle this task properly.
I am using Swing in Scala "org.scala-lang" % "scala-swing" % "2.11.0-M7".
I want to set position for my components explicitly. It is possible to do in Swing API for Java.
Question: is it possible to set absolute position for components in Swing Scala API?
Swing API for Scala example:
import scala.swing._
object PositionAbsolute extends SimpleSwingApplication {
lazy val top = new MainFrame() {
title = "PositionAbsolute"
val label = new Label("I want to be at (0, 0)")
val panel = new FlowPanel()
panel.preferredSize = new swing.Dimension(300, 400)
panel.contents += label
contents = panel
}
}
I know it's a little late for a response - and I am not at all an expert in these things, so please bear with me.
If you absolutely want or need to do absolute positioning of controls with swing in scala, here is a way to do it:
import scala.swing.{Button, Dimension, MainFrame}
object Main extends App {
val b1 = new Button {
text = "one"
preferredSize = new Dimension(60, 30)
}
val b2 = new Button {
text = "two"
preferredSize = new Dimension(80, 40)
}
val b3 = new Button("three")
b1.peer.setBounds(25, 5, b1.peer.getPreferredSize.width, b1.peer.getPreferredSize.height)
b2.peer.setBounds(55, 50, b2.peer.getPreferredSize.width, b2.peer.getPreferredSize.height)
b3.peer.setBounds(150, 15, b3.peer.getPreferredSize.width, b3.peer.getPreferredSize.height)
javax.swing.SwingUtilities.invokeLater(() => {
val frame: MainFrame = new MainFrame {
title = "AbsoluteLayoutDemo"
resizable = true
size = new Dimension(300, 150)
}
frame.peer.setLayout(null)
frame.peer.add(b1.peer)
frame.peer.add(b2.peer)
frame.peer.add(b3.peer)
frame.visible = true
})
}
I don't like it very much myself, but this works.
I compiled this code with scala version 2.13.8 and
libraryDependencies += "org.scala-lang.modules" %% "scala-swing" % "3.0.0"
in the build.sbt file
This was my translation of the java-example in docs.oracle.com/javase/tutorial/uiswing/layout/none.html
but I made a few changes that I thought would make sense for a scala example.
I am not exactly sure what the consequences of this approach are, so please use at your own risk - because I am really not sure how this works.
i cannot seem to reason out why the code below does not zoom to center of the selected objects. usually the objects are near the center but at the top of the screen.
i can use doc.SendStringToExecute("ZOOM " + "OBJECT " + objIdString + " ", true, false, false); and i can get exactly what i want, but not when i want it because the call is from a button on a modeless dialog and is then the last event to occurr.
my objective is to step through and zoom center object selectons pausing with a yes/no to continue dialog between each.
any help will be appreciated.
public static void ZoomObjects(ObjectIdCollection idCol)
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
using (ViewTableRecord view = ed.GetCurrentView())
{
Matrix3d WCS2DCS = Matrix3d.PlaneToWorld(view.ViewDirection);
WCS2DCS = Matrix3d.Displacement(view.Target - Point3d.Origin) * WCS2DCS;
WCS2DCS = Matrix3d.Rotation(-view.ViewTwist, view.ViewDirection, view.Target) * WCS2DCS;
WCS2DCS = WCS2DCS.Inverse();
Entity ent = (Entity)tr.GetObject(idCol[0], OpenMode.ForRead);
Entity ent1 = (Entity)tr.GetObject(idCol[0], OpenMode.ForWrite);
Extents3d ext = ent.GeometricExtents;
for (int i = 1; i < Enerflex.SpoolTagFindForm1.idColCnt; i++)
{
ent = (Entity)tr.GetObject(idCol[i], OpenMode.ForRead);
ent1 = (Entity)tr.GetObject(idCol[i], OpenMode.ForWrite);
Extents3d tmp = ent.GeometricExtents;
ext.AddExtents(tmp);
ent1.Color = Color.FromColor(System.Drawing.Color.Red);
}
ext.TransformBy(WCS2DCS);
view.Width = ext.MaxPoint.X - ext.MinPoint.X;
view.Height = ext.MaxPoint.Y - ext.MinPoint.Y;
view.CenterPoint = new Point2d((ext.MaxPoint.X + ext.MinPoint.X) / 2.0, (ext.MaxPoint.Y + ext.MinPoint.Y) / 2.0);
ed.SetCurrentView(view);
tr.Commit();
}
}
found the answer to the discrepancy with the zoom objects extents. when selected, some objects containing MaxPointX & Y and MinPointX & Y that are identical (text objects) adversely affected the zoom view. when i did not select these objects the zoom worked as expected.
In scala.swing, I can add a component to a container like so:
new Frame {
contents += label
}
but sometimes I'd like to clear the contents of a container and replace them with new components. Based on the docs, I should be able to do:
frame.contents.remove(0)
or
frame.contents.clear
but neither of those compile ("clear/remove is not a member of Seq[scala.swing.Component]").
How can I resolve this? Also, it seems that I can't call frame.contents += blah after intialization. If this is so, how do I add a new component to a Container?
If you're talking about Frame specifically, you can only add one item, so use the method
def contents_= (c: Component) : Unit
and you should be good. Try this out in the REPL (one line at a time so you can see what's going on):
import scala.swing._
val f = new Frame { contents = new Label("Hi") }
f.visible = true
f.contents = new Label("Hey there")
If you're using something that is intended to have multiple items like a BoxPanel, contents is a Buffer so you can add to it and remove from it:
val bp = new BoxPanel(Orientation.Vertical)
val (label1,label2) = (new Label("Hi"), new Label("there"))
bp.contents += label1
bp.contents += label2
f.contents = bp // Now you see "Hi" "there" stacked
bp.contents -= label1
f.pack // "Hi" disappears--need pack not repaint to fix layout
bp.contents += label1
f.pack // "Hi" is back, but at the end
If you have something else like a Component that you're extending, it is your job to override contents with a buffer or have some other way of modifying it (or inherit from SequentialContainer as J-16 said).
contents in Container was a Seq[];
you need a SequentialContainer for that remove method.