I have created two tree control widgets and a scrollbar widget. Now i am trying to create a functionality where the user would have choice of using scrollbar in the sense that there will be radiobutton options which would decide which widget gets scrolled with the scrollbar. I got some idea for how to scroll two widgets together from here. But not sure how to create a switchable scrollbar. My code is below :
package require Tk
package require treectrl
namespace eval ::at::GUI {
variable Priv;
variable OptionsRB;
set Priv(treePrimary) "";
set Priv(treeSecondary) "";
set Priv(treeScrollbar) "";
if { ![info exists OptionsRB] } {
set OptionsRB(scrollTree) "LeftTree";
}
}
proc ::at::GUI::DrawGUI {} {
variable Priv;
set frm_treeFrame [ttk::labelframe .treeFrame -text "Tree Area"];
set Priv(treePrimary) [treectrl $frm_treeFrame.treePrimary]
set Priv(treeSecondary) [treectrl $frm_treeFrame.treeSecondary]
set Priv(treeScrollbar) [ttk::scrollbar $frm_treeFrame.sb_treeScroll -command "::at::GUI::yview"]
grid $Priv(treePrimary) $Priv(treeSecondary) $Priv(treeScrollbar) -sticky news;
grid columnconfigure $frm_treeFrame 0 -weight 1;
grid columnconfigure $frm_treeFrame 1 -weight 1;
grid rowconfigure $frm_treeFrame 0 -weight 1;
set frm_ST [ttk::labelframe .scrollTreeOptions -text "Scroll Option"];
set rb_leftTree [ttk::radiobutton $frm_ST.rb1 -text "Left Tree" -variable [namespace current]::OptionsRB(scrollTree) -value LeftTree -command [namespace current]::configScroll ]
set rb_rightTree [ttk::radiobutton $frm_ST.rb2 -text "Right Tree" -variable [namespace current]::OptionsRB(scrollTree) -value RightTree -command [namespace current]::configScroll ]
set rb_bothTree [ttk::radiobutton $frm_ST.rb3 -text "Both Tree" -variable [namespace current]::OptionsRB(scrollTree) -value BothTree -command [namespace current]::configScroll ]
grid $rb_leftTree -row 1 -padx {30 5} -pady 5 -sticky w
grid $rb_rightTree -row 2 -padx {30 5} -pady 5 -sticky w
grid $rb_bothTree -row 3 -padx {30 5} -pady 5 -sticky w
# Grid all the frames in main window.
grid $frm_treeFrame -sticky news;
grid $frm_ST -sticky news;
##############################
::at::GUI::CreateLemes $Priv(treePrimary)
::at::GUI::CreateLemes $Priv(treeSecondary)
foreach tree [list $Priv(treePrimary) $Priv(treeSecondary)] {
for {set i 0} {$i < 20} {incr i} {
set parent [expr {int(rand()*$i)}]
$tree item create -tag item$i -button auto
$tree item lastchild $parent item$i
$tree item text item$i name item$i
}
}
return;
}
proc ::at::GUI::CreateLemes {T} {
$T element create rect rect -fill [list blue selected]
$T element create name text
set S [$T style create nameStyle]
$T style elements $S {rect name};
$T style layout $S rect -detach yes -iexpand xy;
$T style layout $S name -detach no -iexpand xy -expand e;
$T column create -tag name -itemstyle $S -text Items
$T configure -treecolumn first;
}
proc ::at::GUI::configScroll {args} {
variable Priv;
variable OptionsRB;
if {$OptionsRB(scrollTree) eq "LeftTree"} {
#$Priv(treeSecondary) -yscrollcommand "";
$Priv(treePrimary) -yscrollcommand "::at::GUI::yset $Priv(treeScrollbar)";
} elseif {$OptionsRB(scrollTree) eq "RightTree"} {
#$Priv(treePrimary) -yscrollcommand "";
$Priv(treeSecondary) -yscrollcommand "::at::GUI::yset $Priv(treeScrollbar)";
} elseif {$OptionsRB(scrollTree) eq "BothTree"} {
$Priv(treePrimary) -yscrollcommand "::at::GUI::yset $Priv(treeScrollbar)";
$Priv(treeSecondary) -yscrollcommand "::at::GUI::yset $Priv(treeScrollbar)";
}
}
proc ::at::GUI::yset {sb args} {
uplevel [linsert $args 0 $sb set]
::at::GUI::yview moveto [lindex [$sb get] 0]
}
proc ::at::GUI::yview {args} {
variable Priv;
variable OptionsRB;
if {$OptionsRB(scrollTree) eq "LeftTree"} {
eval [linsert $args 0 $Priv(treePrimary) yview];
} elseif {$OptionsRB(scrollTree) eq "RightTree"} {
eval [linsert $args 0 $Priv(treeSecondary) yview];
} elseif {$OptionsRB(scrollTree) eq "BothTree"} {
eval [linsert $args 0 $Priv(treePrimary) yview];
eval [linsert $args 0 $Priv(treeSecondary) yview];
}
}
The thing is that whenever i try to click on the radiobuttion, i get an error saying :
bad command "-yscrollcommand": must be activate, bbox, canvasx,
canvasy, cget, collapse, column, compare, configure, contentbox,
debug, depth, dragimage, element, expand, gradient, header, identify,
index, item, marquee, notify, numcolumns, numitems, orphans, range,
scan, see, selection, state, style, theme, toggle, xview, or yview bad
command "-yscrollcommand": must be activate, bbox, canvasx, canvasy,
cget, collapse, column, compare, configure, contentbox, debug, depth,
dragimage, element, expand, gradient, header, identify, index, item,
marquee, notify, numcolumns, numitems, orphans, range, scan, see,
selection, state, style, theme, toggle, xview, or yview
while executing "$Priv(treeSecondary) -yscrollcommand "::at::GUI::yset $Priv(treeScrollbar)""
(procedure "::at::GUI::configScroll" line 10)
invoked from within "::at::GUI::configScroll"
invoked from within ".scrollTreeOptions.rb2 invoke "
invoked from within ".scrollTreeOptions.rb2 instate {pressed !disabled} { .scrollTreeOptions.rb2 state !pressed;
.scrollTreeOptions.rb2 invoke } "
(command bound to event)
But the funny part is, even though i get the error, if i move the scrollbar, it does switch between the widget and works as desired. But the error is something i am not able to understand.
This is my very first TK project so I am not sure if I am missing some crucial information. Any comments??
The error message is the key. Indeed the command contained in the Priv(treeSecondary) variable does not have a subcommand named -yscrollcommand. I think you want to use the configure subcommand to change the value of the -yscrollcommand option. Try something like:
$Priv(treeSecondary) configure -yscrollcommand "::at::GUI::yset $Priv(treeScrollbar)"
Related
I am trying to make an upscale texture mod for an old game "Recoil".
For this, I need to find the height, width, pixel format and colour count of a few 1000 images, so that I can feed this information to excel and find the best textures to be upscaled.
I have been able to get the height, width and pixel format via a PowerShell script, which I can then copy to excel as this script provides a table. the script works on the whole folder.
Function Get-Image{
Param(
[Parameter(ValueFromPipeline=$true)]
[System.IO.FileINfo]$file
)
begin{
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") |Out-Null
}
process{
if( $file.Exists){
$img=[System.Drawing.Image]::FromFile($file)
$image=$img.Clone()
$img.Dispose()
$image | Add-Member `
-MemberType NoteProperty `
-Name Filename `
-Value $file.FUllname `
-PassThru
}else{
Write-Host "File not found: $file" -fore yellow
}
}
end{}
}
dir C:\test\*.png | Get-Image
dir C:\test\*.png -Recurse | Get-Image | select filename, Width, Height, PixelFormat | ft -auto
the result of the above code
I need help finding a way to get a colour count of the images. I have found a manual way to do it via a Photoshop filter but it is not a viable way to do all the images.
photoshop filter example
If I can get the Colour count in a similar way to the code provided it would be the best.
edit: I need a way to get Colour count of all images in the folder.
the images themselves are small (the biggest being 512x512). I just need the number of colours, no need for the breakdown of RGB.
ps- I have literally no knowledge of programming and scripting ( even the above script someone Reddit helped me out with)
Hopefully, I have been able to explain my query clearly.
Thank you for your time and consideration.
Edit 2
so this code works but I found an issue. Is there any way to make it not count the alphas?
Issue: colour count difference in the Photoshop Filter (telegraphic- Colour Count) and the new code.
Reason: Photoshop Filter counts colours only (without alpha), and the PowerShell script counts pixels (with alpha).
Format32bppArgb - has the issue
Format24bppRgb - it counts it fine.
below is the present code
Function Get-Image{
Param(
[Parameter(ValueFromPipeline=$true)]
[System.IO.FileINfo]$file
)
begin{
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") |Out-Null
}
process {
if ($file.Exists) {
# Load image
$img = [System.Drawing.Image]::FromFile($file)
$image = $img.Clone()
$img.Dispose()
# Count colors
$colorSet = [System.Collections.Generic.HashSet[System.Drawing.Color]]::new()
foreach ($x in 0..($image.Width - 1)) {
foreach ($y in 0..($image.Height - 1)) {
[void]$colorSet.Add($image.GetPixel($x, $y))
}
}
# Add file name and color count properties to image object
$fileNameProp = #{ MemberType = 'NoteProperty'; Name = 'Filename'; Value = $file.FullName; PassThru = $true}
$colorCountProp = #{ MemberType = 'NoteProperty'; Name = 'ColorCount'; Value = $colorSet.Count; PassThru = $true}
$image | Add-Member #fileNameProp | Add-Member #colorCountProp
}else{
Write-Host "File not found: $file" -fore yellow
}
}
end{}
}
dir D:\Games\Setups\RECOIL_fixed_edition_v0.5\SourceFile\zbd\Dataset_D\Dataset_D\ammoarcgun\*.png | Get-Image
dir D:\Games\Setups\RECOIL_fixed_edition_v0.5\SourceFile\zbd\Dataset_D\Dataset_D\ammoarcgun\*.png -Recurse | Get-Image | select filename, Width, Height, PixelFormat, ColorCount | ft -auto
Use the GetPixel() method to fetch the color used for each pixel, then count the unique colors you encounter:
$hashset = [System.Collections.Generic.HashSet[System.Drawing.Color]]::new()
foreach($x in 0..($image.Width - 1)){
foreach($y in 0..($image.Height - 1)){
[void]$hashset.Add($image.GetPixel($x, $y))
}
}
Write-Host "Image has $($hashset.Count) unique colors"
You could add this routine to your existing function like so:
process {
if ($file.Exists) {
# Load image
$img = [System.Drawing.Image]::FromFile($file)
$image = $img.Clone()
$img.Dispose()
# Count colors
$colorSet = [System.Collections.Generic.HashSet[System.Drawing.Color]]::new()
foreach ($x in 0..($image.Width - 1)) {
foreach ($y in 0..($image.Height - 1)) {
[void]$colorSet.Add($image.GetPixel($x, $y))
}
}
# Add file name and color count properties to image object
$fileNameProp = #{ MemberType = 'NoteProperty'; Name = 'Filename'; Value = $file.FullName; PassThru = $true}
$colorCountProp = #{ MemberType = 'NoteProperty'; Name = 'ColorCount'; Value = $colorSet.Count; PassThru = $true}
$image | Add-Member #fileNameProp | Add-Member #colorCountProp
}
else {
Write-Host "File not found: $file" -fore yellow
}
}
And now you can do:
dir C:\test\*.png -Recurse | Get-Image | ft Filename, Width, Height, PixelFormat, ColorCount -AutoSize
If you want to ignore the alpha-channel in each pixel, change this line:
[void]$colorSet.Add($image.GetPixel($x, $y))
to
$pixel = $image.GetPixel($x, $y)
[void]$colorSet.Add([System.Drawing.Color]::FromArgb($pixel.R, $pixel.G, $pixel.B))
Im having trouble with powershell textbox
here is the definition of it:
$ResultsTextBox = New-Object System.Windows.Forms.TextBox
$ResultsTextBox.Location = New-Object System.Drawing.Size(780,40)
$ResultsTextBox.Size = New-Object System.Drawing.Size(450,340)
i would like to create a function which output text to this text box but i would like to generate the text either red or green
depending of my choice..
but when i do this:
function LinkFn {
$ResultsTextBox.clear()
$SWITCH = Get-ADOrganizationalUnit -filter * -Property CanonicalName | Where-Object {$_.CanonicalName -eq $listBox2.SelectedItem}
forEach ($line in $listBox1.selecteditems){
try {
$ResultsTextBox.ForeColor ='green'
New-GPlink -name $line -target $SWITCH -ErrorAction STOP | Out-null
$ResultsTextBox.AppendText("`n GPO: $line HAVE BEEN LINKED Successfully.`n")
}
catch{
$ResultsTextBox.ForeColor ='red'
$ResultsTextBox.AppendText("`n COULDN NOT LINK GPO: $line `n")
}
}
it changes all the lines, when the result i wanted could be 1 line in red 2 lines in green for example
You need to use a RichTextBox control for this instead of a regular TextBox.
For demo here's a small form that fills lines in different colors to a RichTextBox:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# helper function to write text in a given color
# to the specified RichTextBox control
function Append-ColoredLine {
param(
[Parameter(Mandatory = $true, Position = 0)]
[System.Windows.Forms.RichTextBox]$box,
[Parameter(Mandatory = $true, Position = 1)]
[System.Drawing.Color]$color,
[Parameter(Mandatory = $true, Position = 2)]
[string]$text
)
$box.SelectionStart = $box.TextLength
$box.SelectionLength = 0
$box.SelectionColor = $color
$box.AppendText($text)
$box.AppendText([Environment]::NewLine)
}
$form = New-Object System.Windows.Forms.Form
$form.Width = 400
$form.Height = 500
$richText = New-Object System.Windows.Forms.RichTextBox
$richText.Location = [System.Drawing.Point]::new(10,10)
$richText.Size = [System.Drawing.Size]::new(364,350)
$richText.Font = [System.Drawing.Font]::new('Calibri', 14)
$richText.Anchor = 'Top','Right','Bottom','Left'
$form.Controls.Add($richText)
$button = New-Object System.Windows.Forms.Button
$button.Location = [System.Drawing.Point]::new(10,400)
$button.Size = [System.Drawing.Size]::new(80,30)
$button.Text = 'Test'
$button.Anchor = 'Bottom','Left'
$button.Add_Click({
$richText.Clear()
# write green lines
Append-ColoredLine $richText Green "GPO: 'gpo_A' has been linked Successfully"
Append-ColoredLine $richText Green "GPO: 'gpo_B' has been linked Successfully"
# write red line
Append-ColoredLine $richText Red "Could not link GPO: 'gpo_C'"
# insert blank line
$richText.AppendText([Environment]::NewLine)
# write various lines in different colors
'Blue','DarkGoldenrod','DarkCyan','OliveDrab','Chocolate','Crimson' | ForEach-Object {
Append-ColoredLine $richText $_ "Some text using color '$_'"
}
})
$form.Controls.Add($button)
[void] $form.ShowDialog()
$form.Dispose()
When pressing the Test button on this form, colored lines are written
Hope that helps
I have a powershell function that I need to be able to change. The function centers text in the terminal however, I need to be able to output multiple colors for text on a single line. If I do -NoNewLine and do more Write-host to change the color... then it still calculates the width of the terminal and still adds as much padding as it would without me adding -NoNewLine. Essentially I want my text centered and I want to be able to use multiple colors. With what I have I can only do 1 color per line.
function WriteCentered([String] $text, $color = $null)
{
$width = [int](Get-Host).UI.RawUI.BufferSize.Width
$twidth = [int]$text.Length
$offset = ($width / 2) - ($twidth / 2)
$newText = $text.PadLeft($offset + $twidth)
if($color)
{
Write-Host $newText -ForegroundColor $color
}
else
{
Write-Host $newText
}
}
I have added more IF conditions, I have changed my padding calculations, I am having trouble with getting it just right.
The PowerShell-Module PSWriteColor already does a good job in outputting multiple colors on a single line. Either you download it from GitHub directly and import it with Import-Module <PATH-TO>\PSWriteColor.psd1 or you install it from the PowerShell Gallery directly with Install-Module -Name PSWriteColor.
The syntax in short is Write-Color -Text "GreenText","RedText","BlueText" -Color Green,Red,Blue. So we need to prepend the [String[]]$Text argument with a string containing the necessary whitespace in order to center the message on the screen and prepend a color to the [ConsoleColor[]]$Color argument accordingly.
Here's a little helper function for centering.
#Requires -Modules #{ ModuleName="PSWriteColor"; ModuleVersion="0.8.5" }
function WriteColor-Centered {
param(
[Parameter(Mandatory=$true)][string[]]$Text,
[Parameter(Mandatory=$true)][ConsoleColor[]]$Color
)
$messageLength = 0
$Text | ForEach-Object { $messageLength += $_.Length }
[String[]] $centeredText = "{0}" -f (' ' * (([Math]::Max(0, $Host.UI.RawUI.BufferSize.Width / 2) - [Math]::Floor($messageLength / 2))))
$centeredText += $Text
[ConsoleColor[]]$OutColor = #([ConsoleColor]::White)
$OutColor += $Color
Write-Color -Text $centeredText -Color $OutColor
# Alt.: use WriteColor-Core, see below
# WriteColor-Core -Text $centeredText -Color $OutColor
}
I copied the whitespace calculation from this stackoverflow answer.
EDIT: I was being asked if it's possible to make this work without importing the module. To be honest I feel a little dirty now because I went into source code of a well-written module stripped all the functionality and error handling from it and pasted it here.
Well anyways - if you replace the invocation of Write-Color in the wrapper function above and invoke the following WriteColor-Core instead you can dispense with loading the PSWriteColor module.
function WriteColor-Core {
param(
[Parameter(Mandatory=$true)][string[]]$Text,
[Parameter(Mandatory=$true)][ConsoleColor[]]$Color
)
# Fallback defaults if one of the values isn't set
$LastForegroundColor = [console]::ForegroundColor
# The real deal coloring
for ($i = 0; $i -lt $Text.Count; $i++) {
$CurrentFGColor = if ($Color[$i]) { $Color[$i] } else { $LastForegroundColor }
$WriteParams = #{
NoNewLine = $true
ForegroundColor = $CurrentFGColor
}
Write-Host $Text[$i] #WriteParams
# Store last color set, in case next iteration doesn't have a set color
$LastForegroundColor = $CurrentFGColor
}
Write-Host
}
I am new to using MEL to write scripts.
I have two radio buttons, one and two. When radio button 'two' is selected, I want the script to select the two cube objects I have created in my scene (cube1 and cube2), so that when I use my 'rotate' button (a regular push button), both of the cubes rotate.
On the other hand, if the radio button 'one' is selected, then only one of them (cube1) should rotate when I press the rotate button.
I have my radio buttons as follows:
$radio1 = `radioCollection`;
//my radio buttons
$one = `radioButton -label "1 cube"`;
$two = `radioButton -label "2 cubes"`;
radioCollection -edit -select $one $radio1; //so that this one is selected by default
and for the rotate button I have this that rotates the cube object 'cube1' by 30 degrees. This is currently NOT linked to my radio buttons.
button -label "rotate" -command "setAttr cube1.rotateZ `floatSliderGrp -q -value 30.0`";
Thoughts? Should I query the radio button's state? This would be so much easier for me in another language! I could see myself saying something like "if $radiotwo.pressed, then cube1.rotateZ && cube2.rotateZ"
All Maya UI items are completely imperative: you have to issue commands and get results, there is no 'state': the 'button' or whatever will just be the string name of the object you'll use for issuing the commands
To get the state of the radiocollection you call radioCollection -q -select on the collection, which will return the name of the selected radio button; you'd use that to drive your logic.
string $w = `window`;
string $c = `columnLayout`;
string $radiocol = `radioCollection "radio buttons"`;
string $one_cube = `radioButton -label "1 cube"`;
string $two_cube = `radioButton -label "2 cubes"`;
radioCollection -edit -select $one_cube $radiocol;
showWindow $w;
global proc string get_radio_state(string $radio)
{
string $selected = `radioCollection -q -select $radio`;
return `radioButton -q -label $selected`;
}
print `get_radio_state($radiocol)`;
fiddle with the radio buttons and get_radio_state($radiocol); it should return the name of the selected button.
If you're already familiar with other languages, you should probably skip MEL and jump straight to maya python: it's much more capable and less tweaky. Lots of discussion here and here
For comparison, here's a python version of the same idea:
w = cmds.window()
c =cmds.columnLayout()
rc = cmds.radioCollection()
cmds.radioButton('one', label="1 cube")
cmds.radioButton('two', label = "2 cubes")
def print_selected(*ignore):
print "selected", cmds.radioCollection(rc, q=True, select=True)
btn = cmds.button("print selected", command=print_selected)
cmds.showWindow(w)
Here the button does the same thing as the print statement in the earlier example
Is there any way to create a drop down toolbar button(Like Paste button in MS Word) using Tcl/TK?.I have googled a lot but nothing found.Any help will be appreciable.
You probably want to use a menubutton and a menu, probably with radiobutton entries if I've understood which UI element in Word you're talking about. (Or maybe a ttk::menubutton and menu.) Now, if you're doing something very simple with it then you can make do with just tk_optionMenu as a way to combine those commands, but that's just a simple procedure; if you're doing something complicated with the menu, it's probably easier to write it yourself or at least to get the code for tk_optionMenu and to customise it how you want it to work.
The source code for tk_optionMenu isn't very long; I'll paste the non-comment parts of it here:
proc ::tk_optionMenu {w varName firstValue args} {
upvar #0 $varName var
if {![info exists var]} {
set var $firstValue
}
menubutton $w -textvariable $varName -indicatoron 1 -menu $w.menu \
-relief raised -highlightthickness 1 -anchor c \
-direction flush
menu $w.menu -tearoff 0
$w.menu add radiobutton -label $firstValue -variable $varName
foreach i $args {
$w.menu add radiobutton -label $i -variable $varName
}
return $w.menu
}
You probably want to pay attention to how the menubutton and menu are connected to each other. ttk::menubutton is mostly a drop-in replacement for menubutton, except for different look-and-feel configuration options.